MongoDB 定位 oplog 必须全表扫描吗?
MongoDB oplog (類似于 MySQL binlog) 記錄數據庫的所有修改操作,除了用于主備同步;oplog 還能玩出很多花樣,比如
總的來說,MongoDB 可以通過 oplog 來跟生態對接,來實現數據的同步、遷移、恢復等能力。而在構建這些能力的時候,有一個通用的需求,就是工具或者應用需要有不斷拉取 oplog 的能力;這個過程通常是
那么問題來了,由于 MongoDB oplog 本身沒有索引的,每次定位 oplog 的起點都需要進行全表掃描么?
oplog 的實現細節
{ "ts" : Timestamp(1563950955, 2), "t" : NumberLong(1), "h" : NumberLong("-5936505825938726695"), "v" : 2, "op" : "i", "ns" : "test.coll", "ui" : UUID("020b51b7-15c2-4525-9c35-cd50f4db100d"), "wall" : ISODate("2019-07-24T06:49:15.903Z"), "o" : { "_id" : ObjectId("5d37ff6b204906ac17e28740"), "x" : 0 } } { "ts" : Timestamp(1563950955, 3), "t" : NumberLong(1), "h" : NumberLong("-1206874032147642463"), "v" : 2, "op" : "i", "ns" : "test.coll", "ui" : UUID("020b51b7-15c2-4525-9c35-cd50f4db100d"), "wall" : ISODate("2019-07-24T06:49:15.903Z"), "o" : { "_id" : ObjectId("5d37ff6b204906ac17e28741"), "x" : 1 } } { "ts" : Timestamp(1563950955, 4), "t" : NumberLong(1), "h" : NumberLong("1059466947856398068"), "v" : 2, "op" : "i", "ns" : "test.coll", "ui" : UUID("020b51b7-15c2-4525-9c35-cd50f4db100d"), "wall" : ISODate("2019-07-24T06:49:15.913Z"), "o" : { "_id" : ObjectId("5d37ff6b204906ac17e28742"), "x" : 2 } }上面是 MongoDB oplog 的示例,oplog MongoDB 也是一個集合,但與普通集合不一樣
我們在拉取 oplog 時,第一次從頭開始拉取,然后每次拉取使用完,會記錄最后一條 oplog 的ts字段;如果應用發生重啟,這時需要根據上次拉取的 ts 字段,先找到拉取的起點,然后繼續遍歷。
oplogHack 優化
注:以下實現針對 WiredTiger 存儲引擎,需要 MongoDB 3.0+ 版本才能支持
如果 MongoDB 底層使用的是 WiredTiger 存儲引擎,在存儲 oplog 時,實際上做過優化。MongoDB 會將 ts 字段作為 key,oplog 的內容作為 value,將key-value 存儲到 WiredTiger 引擎里,WiredTiger 默認配置使用 btree 存儲,所以 oplog 的數據在 WT 里實際上也是按 ts 字段順序存儲的,既然是順序存儲,那就有二分查找優化的空間。
MongoDB find 命令提供了一個選項,專門用于優化 oplog 定位。
大致意思是,如果你find的集合是oplog,查找條件是針對 ts 字段的?gte、gt、eq?,那么 MongoDB 字段會進行優化,通過二分查找快速定位到起點; 備節點同步拉取oplog時,實際上就帶了這個選項,這樣備節點每次重啟,都能根據上次同步的位點,快速找到同步起點,然后持續保持同步。
oplogHack 實現
由于咨詢問題的同學對內部實現感興趣,這里簡單的把重點列出來,要深刻理解,還是得深入擼細節。
// src/monogo/db/query/get_executor.cpp StatusWith<unique_ptr<PlanExecutor>> getExecutorFind(OperationContext* txn,Collection* collection,const NamespaceString& nss,unique_ptr<CanonicalQuery> canonicalQuery,PlanExecutor::YieldPolicy yieldPolicy) {// 構建 find 執行計劃時,如果發現有 oplogReplay 選項,則走優化路徑if (NULL != collection && canonicalQuery->getQueryRequest().isOplogReplay()) {return getOplogStartHack(txn, collection, std::move(canonicalQuery));}...return getExecutor(txn, collection, std::move(canonicalQuery), PlanExecutor::YIELD_AUTO, options); } StatusWith<unique_ptr<PlanExecutor>> getOplogStartHack(OperationContext* txn,Collection* collection,unique_ptr<CanonicalQuery> cq) {// See if the RecordStore supports the oplogStartHack// 如果底層引擎支持(WT支持,mmapv1不支持),根據查詢的ts,找到 startLocconst BSONElement tsElem = extractOplogTsOptime(tsExpr);if (tsElem.type() == bsonTimestamp) {StatusWith<RecordId> goal = oploghack::keyForOptime(tsElem.timestamp());if (goal.isOK()) {// 最終調用 src/mongo/db/storage/wiredtiger/wiredtiger_record_store.cpp::oplogStartHackstartLoc = collection->getRecordStore()->oplogStartHack(txn, goal.getValue());}}// Build our collection scan...// 構建全表掃描參數時,帶上 startLoc,真正執行是會快速定位到這個點CollectionScanParams params;params.collection = collection;params.start = *startLoc;params.direction = CollectionScanParams::FORWARD;params.tailable = cq->getQueryRequest().isTailable(); }
原文鏈接
本文為云棲社區原創內容,未經允許不得轉載。
總結
以上是生活随笔為你收集整理的MongoDB 定位 oplog 必须全表扫描吗?的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 重磅开源|AOP for Flutter
- 下一篇: MongoDB 4.2 新特性解读