MongoDB oplog (类似于 MySQL binlog) 记录数据库的所有修改操作,除了用于主备同步;oplog 还能玩出很多花样,比如

  1. 全量备份 + 增量备份所有的 oplog,就能实现 MongoDB 恢复到任意时间点的功能
  2. 通过 oplog,除了实现到备节点的同步,也可以额外再往单独的集群同步数据(甚至是异构的数据库),实现容灾、多活等场景,比如阿里云开源的 MongoShake 就能实现基于 oplog 的增量同步。
  3. MongoDB 3.6+ 版本对 oplog 进行了抽象,提供了 Change Stream 的接口,实际上就是能不断订阅数据库的修改,基于这些修改可以触发一些自定义的事件。
  4. ......

总的来说,MongoDB 可以通过 oplog 来跟生态对接,来实现数据的同步、迁移、恢复等能力。而在构建这些能力的时候,有一个通用的需求,就是工具或者应用需要有不断拉取 oplog 的能力;这个过程通常是

  1. 根据上次拉取的位点构建一个 cursor
  2. 不断迭代 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 也是一个集合,但与普通集合不一样

  1. oplog 是一个 capped collection,但超过配置大小后,就会删除最老插入的数据
  2. oplog 集合没有 id 字段,ts 可以作为 oplog 的唯一标识; oplog 集合的数据本身是按 ts 顺序组织的
  3. 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 字段的 gtegteq ,那么 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 必须全表扫描吗?相关推荐

  1. 深入理解为什么MySQL全表扫描很慢?

    在InnoDB的数据页到底长什么样?这篇文章里,我们讲了InnoDB数据页长什么样,简单回顾下. 图1 InnoDB数据页结构示意图(图片来自网络) 其中有的部分没有详细讲解,因为暂时还用不到.比如P ...

  2. MYSQL避免全表扫描__如何查看sql查询是否用到索引(mysql)

    MYSQL避免全表扫描 1.对查询进行优化,应尽量避免全表扫描,首先应考虑在 where 及 order by 涉及的列上建立索引 2.应尽量避免在 where 子句中对字段进行 null 值判断,否 ...

  3. oracle全表扫过程讲解,CSS_浅谈存取Oracle当中扫描数据的方法,1) 全表扫描(Full Table Scans, FTS) - phpStudy...

    浅谈存取Oracle当中扫描数据的方法 1) 全表扫描(Full Table Scans, FTS) 为实现全表扫描,Oracle读取表中所有的行,并检查每一行是否满足语句的WHERE限制条件一个多块 ...

  4. 记录一次没有收集直方图优化器选择全表扫描导致CPU耗尽

    场景:数据库升级第二天,操作系统CPU使用率接近100%. 查看ash报告: 再看TOP SQL 具体SQL: select count(1) as chipinCount, sum(bets) as ...

  5. oracle查询不走索引全表扫描,使用索引快速全扫描(Index FFS)避免全表扫描的若干场景-Oracle...

    使用索引快速全扫描(Index FFS)避免全表扫描的若干场景 什么使用使用Index FFS比FTS好? Oracle 8的Concept手册中介绍: 1. 索引必须包含所有查询中参考到的列. 2. ...

  6. 我说 SELECT COUNT(*) 会造成全表扫描,面试官让我回去等通知

    来自:码海 前言 上篇 SQL 进阶技巧(下) 中提到使用以下 sql 会导致慢查询 SELECT COUNT(*) FROM SomeTable SELECT COUNT(1) FROM SomeT ...

  7. mysql 全表扫描、全索引扫描、索引覆盖(覆盖索引)

    full index scan:全索引扫描,查询时,遍历索引树来获取数据行.如果数据不是密集的会产生随机IO 在执行计划中是Type列,index full table scan:通过读物理表获取数据 ...

  8. MySQL查询优化:LIMIT 1避免全表扫描

    在某些情况下,如果明知道查询结果只有一个,SQL语句中使用LIMIT 1会提高查询效率. 例如下面的用户表(主键id,邮箱,密码): create table t_user( id int prima ...

  9. 内存只有100G,要全表扫描一个200G大表,会不会把内存用完?

    主机内存只有100G,现在要全表扫描一个200G大表,会不会把DB主机的内存用光? 逻辑备份时,可不就是做整库扫描吗?若这样就会把内存吃光,逻辑备份不是早就挂了? 所以大表全表扫描,看起来应该没问题. ...

最新文章

  1. python 错误 SSLError: [SSL: SSLV3_ALERT_BAD_RECORD_MAC] sslv3 alert bad record mac (_ssl.c:1864) 解决方法
  2. 打印dataframe的前十行_小学生之十行Python解高思五星题(一)
  3. scanf函数使用遇到的问题
  4. oracle的asmcmd获取归档日志,分析oracle的联机日志和归档日志
  5. python3(七)os模块
  6. Sql 存储过程加游标结合的使用
  7. 终于在VirtualBox中装好了Mac OS的虚拟机了!(
  8. 可有可无的Mysql工作技巧
  9. nodejs html引用js_nodejs做出最简单的网页服务端。【501】
  10. 路由器设置虚拟服务器王者人生,路由器设置虚拟服务器王者人生
  11. springboot与docker整合
  12. SQL Server 聚合函数算法优化技巧
  13. oracle 数据分列,如何使用Excel把有规律地txt文本数据分列
  14. K3 Cloud 表体显示序号
  15. ERP管理web后台_数字化、Axure高保真智能化工厂管理系统原型(erp生产管理、仓库管理、采购管理、设备能源管理、计划管理)
  16. 学生信息管理系统—流程图
  17. ​​​​尚硅谷—尚医通—前台用户系统—预约挂号—预约下单功能实现(155-160)
  18. hostapd对WIFI 热点(AP)的配置方法
  19. 浅谈Interpreter解释器模式
  20. 矩阵的LU分解,LU分解的推广,LU分解有什么意义,为什么要用LU分解。

热门文章

  1. 图解Java常用数据结构
  2. python数据处理常用函数_Python大数据处理模块Pandas
  3. python多进程和多线程使用场景_Python36 多线程、多进程的使用场景
  4. php保存复制粘贴的网页内容,JS实现网站内容的禁止复制和粘贴、另存为
  5. mysql是小型数据库_mysql小型数据库
  6. 加载中图片 转圈_对话洛可可平面设计师:平面设计中的效率瓶颈
  7. ubuntu 改linux密码忘了怎么办,Ubuntu 14.04忘记root密码的解决方法
  8. java保存文件到linux指定目录_怎么使用java编程实现linux下全部文件目录的遍历
  9. python打包出现乱码_python解压zip包中文乱码解决方法
  10. 2020,这些前沿技术成全球关注热点