天天看點

MongoDB 如何保證 oplog 順序?

mongodb 複制集裡,主備節點間通過 oplog 來同步資料,priamry 上寫入資料時,會記錄一條oplog,secondary 從 primary 節點拉取 oplog并重放,以保證最終存儲相同的資料集。

oplog 主要特性

幂等性,每一條oplog,重放一次或多次,得到的結果是一樣的;為實作幂等 mongodb 對很多操作進行來轉換,比如将 insert 轉換為 upsert、$inc 操作轉換為 $set等等。

固定大小(capped collection),oplog 使用固定空間存儲,當空間滿了時,會自動删除最舊的文檔。

oplog 按時間戳排序,并且在所有節點上順序保持一緻

本文主要介紹mongodbd 如何保證 oplog 有序存儲并讀取,關于 oplog 擴充閱讀

<a href="https://yq.aliyun.com/articles/54392?spm=5176.8091938.0.0.soobc6">阿裡雲mongodb資料庫的自适應oplog管理</a>

<a href="https://yq.aliyun.com/articles/50138?spm=5176.8091938.0.0.rzfb5e">mongodb 3.2删除優化政策</a>

<a href="https://yq.aliyun.com/articles/47336?spm=5176.8091938.0.0.rm8ccj">mongodb secondary同步慢問題分析</a>

<a href="https://yq.aliyun.com/articles/52404?spm=5176.8091938.0.0.niko9c">mongodb secondary同步慢問題分析(續)</a>

<a href="https://yq.aliyun.com/articles/57755?spm=5176.8091938.0.0.8uuhtx">mongodb同步原了解析</a>

write1

write2

基于上述并發政策,在多個寫并發的情況下,如何保證 oplog 順序?

oplog是一個特殊的 capped collection,文檔沒有_id字段,但包含一個 ts(時間戳字段),所有 oplog 的文檔按照 ts 順序存儲。如下是幾條 oplog 的例子。

以 wiredtiger 為例,在寫入 oplog 文檔時,會以 oplog 的 ts 字段作為 key、文檔内容作為 value,寫入一條 kv 記錄,wiredtiger 會保證存儲(btree 或 lsm 的方式都能保證)的文檔按 key 來排序,這樣就解決『文檔按 ts 字段順序存儲』的問題。但仍然存在并發亂序的問題,例如:

并發寫入多條 oplog時,時間戳分别是ts1、ts2、ts3 (ts1 &lt; ts2 &lt; ts3 ),ts1、ts3先成功了,這時secondary 拉取到這2條 oplog,然後 ts2才寫成功,然後 secondary 再拉取到ts2,也就是說 secondary 看到的 oplog 順序為ts1、ts3、ts2,就會出現 oplog 亂序的問題。

mongodb(wiredtiger 引擎)的解決方案是通過在讀取時進行限制,保證secondary 節點看到一定是順序的,具體實作機制如下:

寫入 oplog前,會先加鎖給 oplog 配置設定時間戳,并注冊到未送出清單裡

lock();

ts = getnextoptime(); // 根據目前時間戳 + 計數器生成

_uncommittedrecordids.insert(ts);

unlock();

正式寫入 oplog,在寫完後,将對應的 oplog 從未送出清單裡移除

writeoplog(ts, oplogdocument);

_uncommittedrecordids.erase(ts);

在拉取 oplog 時

if (_uncommittedrecordids.empty()) {

} else {

}

通過上述規則,最終保證primary 上 oplog 按 ts 字段存儲,并且 secondary能按序讀取所有 oplog。

secondary 把 oplog 拉取到本地後,會多線程重放,最後在一個線程裡将拉取到的 oplog原樣寫入本地的 local.oplog.rs集合,這樣就保證 secondary oplog 最終與 primary 上完全相同。