MongoDB 定位 oplog 必须全表扫描吗?
MongoDB oplog (类似于 MySQL binlog) 记录数据库的所有修改操作,除了用于主备同步;oplog 还能玩出很多花样,比如
- 全量备份 + 增量备份所有的 oplog,就能实现 MongoDB 恢复到任意时间点的功能
- 通过 oplog,除了实现到备节点的同步,也可以额外再往单独的集群同步数据(甚至是异构的数据库),实现容灾、多活等场景,比如阿里云开源的 MongoShake 就能实现基于 oplog 的增量同步。
- MongoDB 3.6+ 版本对 oplog 进行了抽象,提供了 Change Stream 的接口,实际上就是能不断订阅数据库的修改,基于这些修改可以触发一些自定义的事件。
- ......
总的来说,MongoDB 可以通过 oplog 来跟生态对接,来实现数据的同步、迁移、恢复等能力。而在构建这些能力的时候,有一个通用的需求,就是工具或者应用需要有不断拉取 oplog 的能力;这个过程通常是
- 根据上次拉取的位点构建一个 cursor
- 不断迭代 cursor 获取新的 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 是一个 capped collection,但超过配置大小后,就会删除最老插入的数据
- oplog 集合没有 id 字段,ts 可以作为 oplog 的唯一标识; oplog 集合的数据本身是按 ts 顺序组织的
- oplog 没有任何索引字段,通常要找到某条 oplog 要走全表扫描
我们在拉取 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,找到 startLoc
const 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::oplogStartHack
startLoc = 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 必须全表扫描吗?的更多相关文章
- 怎么对10亿数据量级的mongoDB作高效的全表扫描
转自:http://quentinxxz.iteye.com/blog/2149440 一.正常情况下,不应该有这种需求 首先,大家应该有个概念,标题中的这个问题,在大多情况下是一个伪命题,不应该被提 ...
- Oracle 表的访问方式(1) ---全表扫描、通过ROWID访问表
1.Oracle访问表的方式 全表扫描.通过ROWID访问表.索引扫描 2.全表扫描(Full Table Scans, FTS) 为实现全表扫描,Oracle顺序地访问表中每条记录,并检查每一条记录 ...
- Oracle全表扫描
优化器在形成执行计划时需要做的一个重要选择——如何从数据库查询出需要的数据.对于SQL语句存取的任何表中的任何行,可能存在许多存取路径(存取方法),通过它们可以定位和查询出需要的数据.优化器选择其中自 ...
- SQL 数据优化索引建suo避免全表扫描
首先什么是全表扫描和索引扫描?全表扫描所有数据过一遍才能显示数据结果,索引扫描就是索引,只需要扫描一部分数据就可以得到结果.如果数据没建立索引. 无索引的情况下搜索数据的速度和占用内存就会比用索引的检 ...
- 优化一个奇葩表设计上的全表扫描SQL
之前在一个比较繁忙的系统抓到的耗时长.消耗CPU多的一条SQL,如下:SELECT * FROM Z_VISU_DATA_ALARM_LOG TWHERE TO_DATE(T.T_TIMESTR, ' ...
- SQL SERVER中关于OR会导致索引扫描或全表扫描的浅析
在SQL SERVER的查询语句中使用OR是否会导致不走索引查找(Index Seek)或索引失效(堆表走全表扫描 (Table Scan).聚集索引表走聚集索引扫描(Clustered Index ...
- oracle优化:避免全表扫描(高水位线)
如果我们查询了一条SQL语句,这条SQL语句进行了全表扫描,那到底是扫描了多少个数据块呢?是表有多少数据,就扫描多少块吗?不是的.而是扫描高水位线一下的所有块.有的时候有人经常说,我的表也不大呀,怎么 ...
- 想通过加HINT让其走全表扫描
一个SQL,通过SPM固定它的执行计划,可以通过DBMS_SPM.LOAD_PLANS_FROM_CURSOR_CACHE实现.也可以通地此功能在不修改原SQL的情况下对其加HINT来固定执行计划.D ...
- Oracle列操作引起的全表扫描
首先是一种比较明显的情况: select * from table where column + 1 = 2 这里对column进行了列操作,加1以后,与column索引里的内容对不上,导致colum ...
随机推荐
- 菜鸟nginx源码剖析数据结构篇(十一) 共享内存ngx_shm_t[转]
菜鸟nginx源码剖析数据结构篇(十一) 共享内存ngx_shm_t Author:Echo Chen(陈斌) Email:chenb19870707@gmail.com Blog:Blog.csdn ...
- hession RMI 远程调用
/** * * @author administror * 在java中,需要去extends 继承java.rmi.Remote 接口,才能称为在于服务器流的远程对象. * 各客服端调用 * */p ...
- Android基础控件RatingBar星级评分条的使用
1.简介 RatingBar继承ProgressBar,除了ProgressBar的属性外还有特有属性: android:isIndicator:是否用作指示,用户无法更改,默认false andro ...
- linux命令补全工具
一:下载bash-competion工具 https://files.cnblogs.com/files/zgngg/bash-completion.zip 二:解压 unzip bash-comp ...
- 单例模式以及在C#中的使用
下面做一些简要的说明. 1. 单例模式(Singleton Pattern),又称作单件模式,当然也有一种诙谐的称谓:单身模式.在经典的GoF所著的<Design Patterns>一书中 ...
- fcc筆記
在 JavaScript 中所有的变量都是大小写敏感的JavaScript提供七种不同的data types(数据类型),它们是undefined(未定义), null(空), boolean(布尔型 ...
- C语言复制数组
直接上代码,分别是栈上开辟内存,堆上开辟内存 #include <stdio.h> #include <stdlib.h> void test01(){ ]; printf(& ...
- Ionic跳转到外网地址
1.安装插件 https://github.com/apache/cordova-plugin-inappbrowser 执行命令:cordova plugin add org.apache.cord ...
- linux平台进行c语言源码安装
安装c源程序的步骤: 1. ./configure --prefix 执行编译检测 指定安装路径 2. make 编译 3. sudo make install 编译后安装 前两步可以合成一步(mak ...
- app-safeguard-record:record
ylbtech-work-app-safeguard-record:record 1.返回顶部 1. 示数 示数一般是指机械.仪器.仪表.或者需要对数字进行公开的显示的对外数字的宣示.比如电度表(千瓦 ...