天天看点

MongoDB复制集同步原理解析

intial sync,可以理解为全量同步

replication,追同步源的oplog,可以理解为增量同步

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

oplog为空

local.replset.minvalid集合里_initialsyncflag字段设置为true

内存标记initialsyncrequested设置为true

这3个场景分别对应

新节点加入,无任何oplog,此时需先进性initial sync

initial sync开始时,会主动将_initialsyncflag字段设置为true,正常结束后再设置为false;如果节点重启时,发现_initialsyncflag为true,说明上次全量同步中途失败了,此时应该重新进行initial sync

当用户发送resync命令时,initialsyncrequested会设置为true,此时会重新开始一次initial sync

intial sync流程

全量同步开始,设置minvalid集合的_initialsyncflag

获取同步源上最新oplog时间戳为t1

全量同步集合数据 (耗时)

获取同步源上最新oplog时间戳为t2

重放[t1, t2]范围内的所有oplog

获取同步源上最新oplog时间戳为t3

重放[t2, t3]范围内所有的oplog

建立集合所有索引 (耗时)

获取同步源上最新oplog时间戳为t4

重放[t3, t4]范围内所有的oplog

全量同步结束,清除minvalid集合的_initialsyncflag

MongoDB复制集同步原理解析

producer thread,这个线程不断的从同步源上拉取oplog,并加入到一个blockqueue的队列里保存着。

replbatcher thread,这个线程负责逐个从producer thread的队列里取出oplog,并放到自己维护的队列里。

sync线程将replbatcher thread的队列分发到默认16个replwriter线程,由replwriter thread来最终重放每条oplog。

问题来了,为什么一个简单的『拉取oplog并重放』的动作要搞得这么复杂?

为什么不将拉取的oplog直接分发给replwriter thread,而要多一个replbatcher线程来中转?

新加入节点时,可以通过物理复制的方式来避免initial sync,将primary上的dbpath拷贝到新的节点,直接启动,这样效率更高。

生产环境,最好通过db.printslavereplicationinfo()来监控主备同步滞后的情况,当secondary落后太多时,要及时调查清楚原因。

当secondary同步滞后是因为主上并发写入太高导致,(db.serverstatus().metrics.repl.buffer.sizebytes持续接近db.serverstatus().metrics.repl.buffer.maxsizebytes),可通过调整secondary上replwriter并发线程数来提升。