MongoDB的同步原理,官方文档介绍的比较少,网上资料也不是太多,下面是结合官方文档、网上资料和测试时候的日志,整理出来的一点东西。
因为MongoDB的每个分片也是副本集,所以只需要搞副本集的同步原理即可。

一、Initial Sync

大体来说,MongoDB副本集同步主要包含两个步骤:

1. Initial Sync,全量同步
2. Replication,即sync oplog

先通过init sync同步全量数据,再通过replication不断重放Primary上的oplog同步增量数据。全量同步完成后,成员从转换 STARTUP2为SECONDARY

1.1 初始化同步过程

1) 全量同步开始,获取同步源上的最新时间戳t1
2) 全量同步集合数据,建立索引(比较耗时)
3) 获取同步源上最新的时间戳t2
4) 重放t1到t2之间所有的oplog
5) 全量同步结束

简单来说,就是遍历Primary上的所有DB的所有集合,将数据拷贝到自身节点,然后读取全量同步开始到结束时间段内的oplog并重放。

initial sync结束后,Secondary会建立到Primary上local.oplog.rs的tailable cursor,不断从Primary上获取新写入的oplog,并应用到自身。

1.2 初始化同步场景

Secondary节点当出现如下状况时,需要先进⾏全量同步

1) oplog为空
2) local.replset.minvalid集合⾥_initialSyncFlag字段设置为true(用于init sync失败处理)
3) 内存标记initialSyncRequested设置为true(用于resync命令,resync命令只用于master/slave架构,副本集无法使用)

这3个场景分别对应(场景2和场景3没看到官网文档有写,参考张友东大神博客)

1) 新节点加⼊,⽆任何oplog,此时需先进性initial sync
2) initial sync开始时,会主动将_initialSyncFlag字段设置为true,正常结束后再设置为false;如果节点重启时,发现_initialSyncFlag为true,说明上次全量同步中途失败了,此时应该重新进⾏initial sync
3)当⽤户发送resync命令时,initialSyncRequested会设置为true,此时会强制重新开始⼀次initial sync

1.3 疑问点解释

1.3.1 全量同步数据的时候,会不会源数据的oplog被覆盖了导致全量同步失败?

在3.4版本及以后,不会。
下面这张图说明了3.4对全量同步的改进(图来自张友东博客):

官方文档是:

initial sync会在为每个集合复制文档时构所有集合索引。在早期版本(3.4之前)的MongoDB中,仅_id在此阶段构建索引。
Initial sync复制数据的时候会将新增的oplog记录存到本地(3.4新增)。

二、Replication

2.1 sync oplog的过程

全量同步结束后,Secondary就开始从结束时间点建立tailable cursor,不断的从同步源拉取oplog并重放应用到自身,这个过程并不是由一个线程来完成的,mongodb为了提升同步效率,将拉取oplog以及重放oplog分到了不同的线程来执行。
具体线程和作用如下(这部分暂时没有在官方文档找到,来自张友东大神博客):

  • producer thread:这个线程不断的从同步源上拉取oplog,并加入到一个BlockQueue的队列里保存着,BlockQueue最大存储240MB的oplog数据,当超过这个阈值时,就必须等到oplog被replBatcher消费掉才能继续拉取。
  • replBatcher thread:这个线程负责逐个从producer thread的队列里取出oplog,并放到自己维护的队列里,这个队列最多允许5000个元素,并且元素总大小不超过512MB,当队列满了时,就需要等待oplogApplication消费掉
  • oplogApplication会取出replBatch thread当前队列的所有元素,并将元素根据docId(如果存储引擎不支持文档锁,则根据集合名称)分散到不同的replWriter线程,replWriter线程将所有的oplog应用到自身;等待所有oplog都应用完毕,oplogApplication线程将所有的oplog顺序写入到local.oplog.rs集合。

针对上面的叙述,画了一个图方便理解:

producer的buffer和apply线程的统计信息都可以通过db.serverStatus().metrics.repl来查询到。

2.2 对过程疑问点的解释

2.2.1 为什么oplog的回放要弄这么多的线程?

和mysql一样,一个线程做一个事情,拉取oplog是单线程,其他线程进行回放;多个回放线程加快速度。

2.2.2 为什么需要replBatcher线程来中转?

oplog重放时,要保持顺序性,⽽且遇到create、drop等DDL命令时,这些命令与其他的增删改查命令是不能并⾏执⾏的,⽽这些控制就是由replBatcher来完成的。

2.2.3 如何解决secondary节点oplog重放追不上primary问题?

方法一:设置更大的回放线程数

  * mongod命令行指定:mongod --setParameter replWriterThreadCount=32* 配置文件中指定
setParameter:replWriterThreadCount: 32

方法二:增大oplog的大小
方法三:将writeOpsToOplog步骤分散到多个replWriter线程来并发执行,看官方开发者日志已经实现了这个(在3.4.0-rc2版本)

2.3 注意事项

  • initial sync单线程复制数据,效率比较低,生产环境应该尽量避免initial sync出现,需合理配置oplog。
  • 新加⼊节点时,可以通过物理复制的⽅式来避免initial sync,将Primary上的dbpath拷⻉到新的节点,然后直接启动。
  • 当Secondary同步滞后是因为主上并发写入太高导致,db.serverStatus().metrics.repl.buffer的 sizeBytes值持续接近maxSizeBytes的时候,可通过调整Secondary上replWriter并发线程数来提升。

三、日志分析

3.1 初始化同步日志

将日志级别 verbosity设置为 1,然后过滤日志
cat mg36000.log |egrep "clone|index|oplog" >b.log
最后拿出过滤后的部分日志。
3.4.21新加入节点日志

因为日志太多,贴太多出来也没什么意义,下面贴出了对db01库的某个
集合的日志。
可以发现是先创建collection索引,然后clone集合数据和索引数据,这样就完成了该集合的clone。最后将配置改为下一个集合。
2019-08-21T16:50:10.880+0800 D STORAGE  [InitialSyncInserters-db01.test20] create uri: table:db01/index-27-154229953453504826 config: type=file,internal_page_max=16k,leaf_page_max=16k,checksum=on,prefix_compression=true,block_compressor=,,,,key_format=u,value_format=u,app_metadata=(formatVersion=8,infoObj={ "v" : 2, "key" : { "num" : 1 }, "name" : "num_1", "ns" : "db01.test2" }),
2019-08-21T16:50:10.882+0800 I INDEX    [InitialSyncInserters-db01.test20] build index on: db01.test2 properties: { v: 2, key: { num: 1.0 }, name: "num_1", ns: "db01.test2" }
2019-08-21T16:50:10.882+0800 I INDEX    [InitialSyncInserters-db01.test20]      building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2019-08-21T16:50:10.882+0800 D STORAGE  [InitialSyncInserters-db01.test20] create uri: table:db01/index-28-154229953453504826 config: type=file,internal_page_max=16k,leaf_page_max=16k,checksum=on,prefix_compression=true,block_compressor=,,,,key_format=u,value_format=u,app_metadata=(formatVersion=8,infoObj={ "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "db01.test2" }),
2019-08-21T16:50:10.886+0800 I INDEX    [InitialSyncInserters-db01.test20] build index on: db01.test2 properties: { v: 2, key: { _id: 1 }, name: "_id_", ns: "db01.test2" }
2019-08-21T16:50:10.886+0800 I INDEX    [InitialSyncInserters-db01.test20]      building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2019-08-21T16:50:10.901+0800 D INDEX    [InitialSyncInserters-db01.test20]      bulk commit starting for index: num_1
2019-08-21T16:50:10.906+0800 D INDEX    [InitialSyncInserters-db01.test20]      bulk commit starting for index: _id_
2019-08-21T16:50:10.913+0800 D REPL     [repl writer worker 11] collection clone finished: db01.test2
2019-08-21T16:50:10.913+0800 D REPL     [repl writer worker 11]     collection: db01.test2, stats: { ns: "db01.test2", documentsToCopy: 2000, documentsCopied: 2000, indexes: 2, fetchedBatches: 1, start: new Date(1566377410875), end: new Date(1566377410913), elapsedMillis: 38 }
2019-08-21T16:50:10.920+0800 D STORAGE  [InitialSyncInserters-db01.collection10] create uri: table:db01/index-30-154229953453504826 config: type=file,internal_page_max=16k,leaf_page_max=16k,checksum=on,prefix_compression=true,block_compressor=,,,,key_format=u,value_format=u,app_metadata=(formatVersion=8,infoObj={ "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "db01.collection1" }),

3.6.12加入新节点日志

3.6较3.4的区别是,复制数据库的线程明确了是:repl writer worker 进行重放(看文档其实3.4已经是如此了)
还有就是明确是用cursors来进行。
其他和3.4没有区别,也是创建索引,然后clone数据。
2019-08-22T13:59:39.444+0800 D STORAGE  [repl writer worker 9] create uri: table:db01/index-32-3334250984770678501 config: type=file,internal_page_max=16k,leaf_page_max=16k,checksum=on,prefix_compression=true,block_compressor=,,,,key_format=u,value_format=u,app_metadata=(formatVersion=8,infoObj={ "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "db01.collection1" }),log=(enabled=true)
2019-08-22T13:59:39.446+0800 I INDEX    [repl writer worker 9] build index on: db01.collection1 properties: { v: 2, key: { _id: 1 }, name: "_id_", ns: "db01.collection1" }
2019-08-22T13:59:39.446+0800 I INDEX    [repl writer worker 9]      building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2019-08-22T13:59:39.447+0800 D REPL     [replication-1] Collection cloner running with 1 cursors established.
2019-08-22T13:59:39.681+0800 D INDEX    [repl writer worker 7]      bulk commit starting for index: _id_
2019-08-22T13:59:39.725+0800 D REPL     [repl writer worker 7] collection clone finished: db01.collection1
2019-08-22T13:59:39.725+0800 D REPL     [repl writer worker 7]     database: db01, stats: { dbname: "db01", collections: 1, clonedCollections: 1, start: new Date(1566453579439), end: new Date(1566453579725), elapsedMillis: 286 }
2019-08-22T13:59:39.725+0800 D REPL     [repl writer worker 7]     collection: db01.collection1, stats: { ns: "db01.collection1", documentsToCopy: 50000, documentsCopied: 50000, indexes: 1, fetchedBatches: 1, start: new Date(1566453579440), end: new Date(1566453579725), elapsedMillis: 285 }
2019-08-22T13:59:39.731+0800 D STORAGE  [repl writer worker 8] create uri: table:test/index-34-3334250984770678501 config: type=file,internal_page_max=16k,leaf_page_max=16k,checksum=on,prefix_compression=true,block_compressor=,,,,key_format=u,value_format=u,app_metadata=(formatVersion=8,infoObj={ "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "test.user1" }),log=(enabled=true)

4.0.11加入新节点日志

使用cursors,和3.6基本一致
2019-08-22T15:02:13.806+0800 D STORAGE  [repl writer worker 15] create uri: table:db01/index-30--463691904336459055 config: type=file,internal_page_max=16k,leaf_page_max=16k,checksum=on,prefix_compression=true,block_compressor=,,,,key_format=u,value_format=u,app_metadata=(formatVersion=8,infoObj={ "v" : 2, "key" : { "num" : 1 }, "name" : "num_1", "ns" : "db01.collection1" }),log=(enabled=false)
2019-08-22T15:02:13.816+0800 I INDEX    [repl writer worker 15] build index on: db01.collection1 properties: { v: 2, key: { num: 1.0 }, name: "num_1", ns: "db01.collection1" }
2019-08-22T15:02:13.816+0800 I INDEX    [repl writer worker 15]      building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2019-08-22T15:02:13.816+0800 D STORAGE  [repl writer worker 15] create uri: table:db01/index-31--463691904336459055 config: type=file,internal_page_max=16k,leaf_page_max=16k,checksum=on,prefix_compression=true,block_compressor=,,,,key_format=u,value_format=u,app_metadata=(formatVersion=8,infoObj={ "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "db01.collection1" }),log=(enabled=false)
2019-08-22T15:02:13.819+0800 I INDEX    [repl writer worker 15] build index on: db01.collection1 properties: { v: 2, key: { _id: 1 }, name: "_id_", ns: "db01.collection1" }
2019-08-22T15:02:13.819+0800 I INDEX    [repl writer worker 15]      building index using bulk method; build may temporarily use up to 500 megabytes of RAM
2019-08-22T15:02:13.820+0800 D REPL     [replication-0] Collection cloner running with 1 cursors established.

3.2 复制日志

2019-08-22T15:15:17.566+0800 D STORAGE  [repl writer worker 2] create collection db01.collection2 { uuid: UUID("8e61a14e-280c-4da7-ad8c-f6fd086d9481") }
2019-08-22T15:15:17.567+0800 I STORAGE  [repl writer worker 2] createCollection: db01.collection2 with provided UUID: 8e61a14e-280c-4da7-ad8c-f6fd086d9481
2019-08-22T15:15:17.567+0800 D STORAGE  [repl writer worker 2] stored meta data for db01.collection2 @ RecordId(22)
2019-08-22T15:15:17.580+0800 D STORAGE  [repl writer worker 2] db01.collection2: clearing plan cache - collection info cache reset
2019-08-22T15:15:17.580+0800 D STORAGE  [repl writer worker 2] create uri: table:db01/index-43--463691904336459055 config: type=file,internal_page_max=16k,leaf_page_max=16k,checksum=on,prefix_compression=true,block_compressor=,,,,key_format=u,value_format=u,app_metadata=(formatVersion=8,infoObj={ "v" : 2, "key" : { "_id" : 1 }, "name" : "_id_", "ns" : "db01.collection2" }),log=(enabled=false)

原文链接
本文为云栖社区原创内容,未经允许不得转载。

MongoDB副本集同步原理相关推荐

  1. MongoDB副本集同步设置记录

    主从同步操作========================================== 我在mongod4.0运行时提示:[main] Master/slave replication is ...

  2. (2)MongoDB副本集自动故障转移原理(含客户端)

    前文我们搭建MongoDB三成员副本集,了解集群基本特性,今天我们围绕下图聊一聊背后的细节. 默认搭建的副本集均在主节点读写,辅助节点冗余部署,形成高可用和备份,具备自动故障转移能力. 集群心跳保活 ...

  3. MongoDB副本集详解与搭建

    1. 简介 MongoDB中的副本集(Replica Set)是一组维护相同数据集的mongod服务. 副本集可提供冗余和高可用性,是所有生产部署的基础.也可以说,副本集类似于有自动故障恢复功能的主从 ...

  4. MongoDB副本集搭建(超级详细)

    MongoDB 副本集原理 什么是复制 复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并可以保证数据的安全性 复制还允许从硬件故障和服务中断中恢复数据 为什么要复制 数据 ...

  5. Mongodb 副本集+分片集群搭建

    环境需求: 在三台机器上搭建副本集,在副本集上做分片 服务器IP: 192.168.1.232/192.168.1.238/192.168.1.241 安装Mongodb all nodes: tar ...

  6. MongoDB副本集权限重置

    背景:某公司线上的一套MongoDB副本集,只知道一个应用库以及该库下对应的一个用户.除此以外,对这套集群一无所知,他们的需求是要拥有超级用户权限,方便以后管理. 我虽是一个小渣渣,但以前单位一个很照 ...

  7. MongoDB副本集学习(一):概述和环境搭建

    MongoDB副本集概述 以下图片摘自MongoDB官方文档:http://docs.mongodb.org/manual/core/replication-introduction/ Primary ...

  8. mongodb副本集维护

    一.概述 mongodb副本集维护主要工作: 1.查看副本集状态(集群状态.同步延迟.单个库的运行状态mongostate) 2.增删节点.停节点shutdown mongodb副本集集群同步机制 数 ...

  9. mongodb副本集+分片集群部署 step by step

    mongodb副本集+分片集群部署step by step 本文只讲述mongodb副本集+分片集群的部署,关于mongdb shading & replica set原理优点等不在本文讨论范 ...

最新文章

  1. oracle 存储 更新,oracle 更新空间数据存储过程语句
  2. 「Python」超简单!Python返回矩阵最大元素/最小值坐标,三行搞定!(更新:一行搞定!)
  3. 纯CSS无hacks的跨游览器多列布局(转)
  4. 连接MySql出现异常解决:java.sql.SQLException: The server time zone value is unrecognized or represents。
  5. SQL SERVER 与ACCESS、EXCEL的数据导入导出转换
  6. tomcat中三种部署项目的方法(转)
  7. Ubuntu 12.04忘记登录密码及修改密码
  8. nideshop小程序商城部署
  9. 使用npm和命令行强制删除文件
  10. v-charts使用-实例
  11. 上海航芯 | 全自动咖啡机设计方案
  12. 配置七牛云cdn加速
  13. 多元思考科学决策!建立高品质思维的30种模型
  14. 6秒了解给kindle传书
  15. 结构方程模型(SEM)高阶应用系列
  16. Php字符拼出心形,canvas实现九宫格心形拼图的方法(附代码)-
  17. 从上海回杭州三年,我的生活发生了翻天覆地的变化
  18. 做个网站要多少钱,怎么收费的?
  19. Word 批量转 PDF 方法, 杜绝耍手段的交费软件!
  20. VS2013 + Qt 配置指南

热门文章

  1. python工业自动化仿真_ABAQUS 中基于 Python 脚本语言开发实现仿真自动化操作
  2. ubuntu18.04升级python_Ubuntu18.04一次性升级Python所有库的方法步骤
  3. web 服务器 内存 影响_C/C++服务器开发常用的7大开源库,让你在同行中脱颖而出...
  4. opcua 入门简介 java_大二的学生自学Java有出路吗?
  5. 【LeetCode笔记】剑指 Offer 65. 不用加减乘除做加法(Java、位运算、二刷)
  6. python条件循环叠加_Python基础:条件判断与循环的两个要点
  7. xml建模包括以下_为什么要进行建模仿真?
  8. c语言程序设计第2章,C语言程序设计第2章 结构化程序设计与算法.ppt
  9. 多路平衡查找树 --- B(B-)树
  10. python编写add函数求和_为什么python不利用__iadd__来实现求和和链接运算符?