天天看点

MongoDB Secondary同步慢问题分析MongoDB Scondary同步慢问题分析

最近生产环境出现多次primary写入qps太高,导致seconary的同步无法跟上的问题(secondary上的最新oplog时间戳比primary上最旧oplog时间戳小),使得secondary变成recovering状态,这时需要人工介入处理,向secondary发送resync命令,让secondary重新全量同步一次。

下图是mongodb数据同步的流程

MongoDB Secondary同步慢问题分析MongoDB Scondary同步慢问题分析

新节点加入(或者主动向secondary发送resync)时,secondary会先进行一次initial sync,即全量同步,遍历primary上的所有db的所有集合,将数据拷贝到自身节点,然后读取『全量同步开始到结束时间段内』的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来查询到,在测试过程中,向primary模拟约10000 qps的写入,观察secondary上的同步,写入速率远小于primary,大致只有3000左右的qps,同时观察到<code>producer的buffer很快就达到饱和,可以判断出oplog重放的线程跟不上</code>。

默认情况下,secondary采用16个replwriter线程来重放oplog,可通过启动时设置replwriterthreadcount参数来定制线程数,当提升线程数到32时,同步的情况大大改观,主备写入的qps基本持平,主备上数据同步的延时控制在1s以内,进一步验证了上述结论。

如果因primary上的写入qps很高,经常出现secondary同步无法追上的问题,可以考虑以下改进思路

配置更高的replwriterthreadcount,secondary上加速oplog重放,代价是更高的内存开销

附修改replwriterthreadcount参数的方法,具体应该调整到多少跟primary上的写入负载如写入qps、平均文档大小等相关,并没有统一的值。

通过mongod命令行来指定

mongod --setparameter replwriterthreadcount=32

在配置文件中指定

<a href="https://docs.mongodb.com/manual/core/capped-collections/">capped collection</a>

<a href="https://jira.mongodb.org/browse/server-18908">server-18908</a>

<a href="https://docs.mongodb.com/v3.0/tutorial/change-oplog-size/">修改oplog的大小</a>

<a href="https://www.aliyun.com/product/mongodb/?spm=5176.7960203.237031.33.ydynry">阿里云mongodb数据库</a>