標籤:style blog http io ar color os 使用 sp
在MongoDB複本集中,主節點負責處理用戶端的讀寫請求,備份節點則負責映射主節點的資料。
備份節點的工作原理過程可以大致描述為,備份節點定期輪詢主節點上的資料操作,然後對自己的資料副本進行這些操作,從而保證跟主節點的資料同步。
至於主節點上的所有資料庫狀態改變的操作,都會存放在一張特定的系統資料表中。備份節點則是根據這些資料進行自己的資料更新。
oplog
上面提到的資料庫狀態改變的操作,稱為oplog(operation log,主節點操作記錄)。oplog儲存在local資料庫的"oplog.rs"表中。複本集中備份節點非同步從主節點同步oplog,然後重新執行它記錄的操作,以此達到了資料同步的作用。
關於oplog有幾個注意的地方:
- oplog只記錄改變資料庫狀態的操作
- 儲存在oplog中的操作並不是和主節點執行的操作完全一樣,例如"$inc"操作就會轉化為"$set"操作
- oplog儲存在固定集合中(capped collection),當oplog的數量超過oplogSize,新的操作就會覆蓋就的操作
下面來看下oplog的一些具體內容,首先刪除上一篇的node1-node3檔案夾,重建立立複本集,但是這次限制oplogSize為5MB。
mongod.exe --dbpath="c:\mongodb\db\node1" --port=11111 --replSet myReplSet --oplogSize=5mongod.exe --dbpath="c:\mongodb\db\node2" --port=22222 --replSet myReplSet --oplogSize=5mongod.exe --dbpath="c:\mongodb\db\node3" --port=33333 --replSet myReplSet --oplogSize=5
然後通過MongoDB shell(串連主節點)插入一些資料
use testdb.person.insert({ "name" : "Will0", "gender" : "Female", "age" : 22 })db.person.insert({ "name" : "Will1", "gender" : "Female", "age" : 20 })
通過一些命令就可以查看主節點的oplog了,通過oplog可以看到前面兩條資料插入操作,備份節點接可以根據這兩條記錄更新自己的資料集。
use localshow collectionsdb.oplog.rs.find()
查看oplog表的狀態,當前oplog有3條記錄,oplog表是一個capped collection(固定大小集合),oplog表的大小是5242880B=5MB。
oplog資料結構
下面來分析一下oplog中欄位的含義,通過下面的命令取出一條oplog:
db.oplog.rs.find().skip(1).limit(1).toArray()
- ts: 8位元組的時間戳記,由4位元組unix timestamp + 4位元組自增計數表示。這個值很重要,在選舉(如master宕機時)新primary時,會選擇ts最大的那個secondary作為新primary
- op:1位元組的操作類型
- "i": insert
- "u": update
- "d": delete
- "c": db cmd
- "db":聲明當前資料庫 (其中ns 被設定成為=>資料庫名稱+ ‘.‘)
- "n": no op,即空操作,其會定期執行以確保時效性
- ns:操作所在的namespace
- o:操作所對應的document,即當前操作的內容(比如更新操作時要更新的的欄位和值)
- o2: 在執行更新操作時的where條件,僅限於update時才有該屬性
oplog的大小
capped collection是MongoDB中一種提供高效能插入、讀取和刪除操作的固定大小集合,當集合被填滿的時候,新的插入的文檔會覆蓋老的文檔。
所以,oplog表使用capped collection是合理的,因為不可能無限制的增長oplog。MongoDB在初始化複本集的時候都會有一個預設的oplog大小:
- 在64位的Linux,Solaris,FreeBSD以及Windows系統上,MongoDB會分配磁碟剩餘空間的5%作為oplog的大小,如果這部分小於1GB則分配1GB的空間
- 在64的OS X系統上會分配183MB
- 在32位的系統上則只分配48MB
oplog的大小設定是值得考慮的一個問題,如果oplog size過大,會浪費儲存空間;如果oplog size過小,老的oplog記錄很快就會被覆蓋,那麼宕機的節點就很容易出現無法同步資料的現象。
比如,基於上面的例子,我們停掉一個備份節點(port=33333),然後通過主節點插入以下記錄,然後查看oplog,發現以前的oplog已經被覆蓋了。
for(var i=0;i<10000;i++){ var randAge = parseInt(5*Math.random()) + 20; var gender = (randAge%2)?"Male":"Female"; db.school.students.insert({"name":"Will"+i, "gender": gender, "age": randAge});}
接下來重新啟動上面停掉的備份節點(port=33333),從server的輸出中可以看到,oplog已經太新了,備份節點無法進行同步了。
通過MongoDB shell串連上這個節點,會發現這個節點一直處於RECOVERING狀態。
資料同步
在複本集中,有兩種資料同步方式:
- initial sync(初始化):這個過程發生在當複本集中建立一個新的資料庫或其中某個節點剛從宕機中恢複,或者向複本集中添加新的成員的時候,預設的,複本集中的節點會從離它最近的節點複製oplog來同步資料,這個最近的節點可以是primary也可以是擁有最新oplog副本的secondary節點。
- replication(複製):在初始化後這個操作會一直持續的進行著,以保持各個secondary節點之間的資料同步。
initial sync
當遇到上面例子中無法同步的問題時,只能使用以下兩種方式進行initial sync了
- 第一種方式就是停止該節點,然後刪除目錄中的檔案,重新啟動該節點。這樣,這個節點就會執行initial sync
- 注意:通過這種方式,sync的時間是根據資料量大小的,如果資料量過大,sync時間就會很長
- 同時會有很多網路傳輸,可能會影響其他節點的工作
- 第二種方式,停止該節點,然後刪除目錄中的檔案,找一個比較新的節點,然後把該節點目錄中的檔案拷貝到要sync的節點目錄中
通過上面兩種方式中的一種,都可以重新恢複"port=33333"的節點。不在進行了。
複本集管理查看oplog的資訊
通過"db.printReplicationInfo()"命令可以查看oplog的資訊
欄位說明:
- configured oplog size: oplog檔案大小
- log length start to end: oplog日誌的啟用時間段
- oplog first event time: 第一個交易記錄的產生時間
- oplog last event time: 最後一個交易記錄的產生時間
- now: 現在的時間
查看slave狀態
通過"db.printSlaveReplicationInfo()"可以查看slave的同步狀態
當我們插入一條新的資料,然後重新檢查slave狀態時,就會發現sync時間更新了
總結
在這篇文章中介紹了複本集的工作原理,通過oplog以及資料同步進一步瞭解了複本集。
另外,實踐中難免會碰到需要修改oplog size的情況,本篇文章沒有進行介紹,請參考MongoDB文檔中的步驟,修改oplog size
Ps:例子中所有的命令都可以參考以下連結
http://files.cnblogs.com/wilber2013/oplog.js
MongoDB複本集的工作原理