MongoDB副本集同步原理
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副本集同步原理相关推荐
- MongoDB副本集同步设置记录
主从同步操作========================================== 我在mongod4.0运行时提示:[main] Master/slave replication is ...
- (2)MongoDB副本集自动故障转移原理(含客户端)
前文我们搭建MongoDB三成员副本集,了解集群基本特性,今天我们围绕下图聊一聊背后的细节. 默认搭建的副本集均在主节点读写,辅助节点冗余部署,形成高可用和备份,具备自动故障转移能力. 集群心跳保活 ...
- MongoDB副本集详解与搭建
1. 简介 MongoDB中的副本集(Replica Set)是一组维护相同数据集的mongod服务. 副本集可提供冗余和高可用性,是所有生产部署的基础.也可以说,副本集类似于有自动故障恢复功能的主从 ...
- MongoDB副本集搭建(超级详细)
MongoDB 副本集原理 什么是复制 复制提供了数据的冗余备份,并在多个服务器上存储数据副本,提高了数据的可用性,并可以保证数据的安全性 复制还允许从硬件故障和服务中断中恢复数据 为什么要复制 数据 ...
- Mongodb 副本集+分片集群搭建
环境需求: 在三台机器上搭建副本集,在副本集上做分片 服务器IP: 192.168.1.232/192.168.1.238/192.168.1.241 安装Mongodb all nodes: tar ...
- MongoDB副本集权限重置
背景:某公司线上的一套MongoDB副本集,只知道一个应用库以及该库下对应的一个用户.除此以外,对这套集群一无所知,他们的需求是要拥有超级用户权限,方便以后管理. 我虽是一个小渣渣,但以前单位一个很照 ...
- MongoDB副本集学习(一):概述和环境搭建
MongoDB副本集概述 以下图片摘自MongoDB官方文档:http://docs.mongodb.org/manual/core/replication-introduction/ Primary ...
- mongodb副本集维护
一.概述 mongodb副本集维护主要工作: 1.查看副本集状态(集群状态.同步延迟.单个库的运行状态mongostate) 2.增删节点.停节点shutdown mongodb副本集集群同步机制 数 ...
- mongodb副本集+分片集群部署 step by step
mongodb副本集+分片集群部署step by step 本文只讲述mongodb副本集+分片集群的部署,关于mongdb shading & replica set原理优点等不在本文讨论范 ...
最新文章
- oracle 存储 更新,oracle 更新空间数据存储过程语句
- 「Python」超简单!Python返回矩阵最大元素/最小值坐标,三行搞定!(更新:一行搞定!)
- 纯CSS无hacks的跨游览器多列布局(转)
- 连接MySql出现异常解决:java.sql.SQLException: The server time zone value is unrecognized or represents。
- SQL SERVER 与ACCESS、EXCEL的数据导入导出转换
- tomcat中三种部署项目的方法(转)
- Ubuntu 12.04忘记登录密码及修改密码
- nideshop小程序商城部署
- 使用npm和命令行强制删除文件
- v-charts使用-实例
- 上海航芯 | 全自动咖啡机设计方案
- 配置七牛云cdn加速
- 多元思考科学决策!建立高品质思维的30种模型
- 6秒了解给kindle传书
- 结构方程模型(SEM)高阶应用系列
- Php字符拼出心形,canvas实现九宫格心形拼图的方法(附代码)-
- 从上海回杭州三年,我的生活发生了翻天覆地的变化
- 做个网站要多少钱,怎么收费的?
- Word 批量转 PDF 方法, 杜绝耍手段的交费软件!
- VS2013 + Qt 配置指南
热门文章
- python工业自动化仿真_ABAQUS 中基于 Python 脚本语言开发实现仿真自动化操作
- ubuntu18.04升级python_Ubuntu18.04一次性升级Python所有库的方法步骤
- web 服务器 内存 影响_C/C++服务器开发常用的7大开源库,让你在同行中脱颖而出...
- opcua 入门简介 java_大二的学生自学Java有出路吗?
- 【LeetCode笔记】剑指 Offer 65. 不用加减乘除做加法(Java、位运算、二刷)
- python条件循环叠加_Python基础:条件判断与循环的两个要点
- xml建模包括以下_为什么要进行建模仿真?
- c语言程序设计第2章,C语言程序设计第2章 结构化程序设计与算法.ppt
- 多路平衡查找树 --- B(B-)树
- python编写add函数求和_为什么python不利用__iadd__来实现求和和链接运算符?