阿里云数据库ApsaraDB 关注
手机版
  1. 云栖社区>
  2. 阿里云数据库ApsaraDB>
  3. 博客>
  4. 正文

MongoDB Secondary同步慢问题分析

yunnotes 2016-05-18 14:48:25 浏览5271 评论3 发表于: 阿里云数据库ApsaraDB

数据存储与数据库 云存储技术 MongoDB

摘要: MongoDB Scondary同步慢问题分析 问题背景 最近生产环境出现多次Primary写入QPS太高,导致Seconary的同步无法跟上的问题(Secondary上的最新oplog时间戳比Primary上最旧oplog时间戳小),使得Secondary变成RECOVERING状态,这时需要

MongoDB Scondary同步慢问题分析

问题背景

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

同步过程

下图是MongoDB数据同步的流程

Primary上的写入会记录oplog,存储到一个固定大小的capped collection里,Secondary主动从Primary上拉取oplog并重放应用到自身,以保持数据与Primary节点上一致。

initial sync

新节点加入(或者主动向Secondary发送resync)时,Secondary会先进行一次initial sync,即全量同步,遍历Primary上的所有DB的所有集合,将数据拷贝到自身节点,然后读取『全量同步开始到结束时间段内』的oplog并重放。全量同步不是本文讨论的重点,将不作过多的介绍。

tailing 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,同时观察到producer的buffer很快就达到饱和,可以判断出oplog重放的线程跟不上

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

改进思路

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

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

  1. 通过mongod命令行来指定

     mongod --setParameter replWriterThreadCount=32
    
  2. 在配置文件中指定

    setParameter:
        replWriterThreadCount: 32
    

参考资料

本文为云栖社区原创内容,未经允许不得转载,如需转载请发送邮件至yqeditor@list.alibaba-inc.com;如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件至:yqgroup@service.aliyun.com 进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容。

用云栖社区APP,舒服~

【云栖快讯】浅析混合云和跨地域网络构建实践,分享高性能负载均衡设计,9月21日阿里云专家和你说说网络那些事儿,足不出户看直播,赶紧预约吧!  详情请点击

网友评论

1F
jakeruan

你好,文章里说到(具体位置在tailing oplog下方),replBatcher thread,这个线程负责逐个从producer thread的队列里取出oplog,并放到自己维护的队列里。。。

放到自己队列里,是数据的一次完全拷贝吗?如果是,这里可以使用mmap吗?或者直接把BlockQueue队列在原有基础上搞大一些,所有的消费者线程replBatcher直接去消费(通过tag标记数据是否已被处理)。

其次,replWriterThreadCount默认开16个线程,当出现延迟较大时,建议调高此值。虽然线程切换带来开销小很多,but。。。。
而且一个集合只能有一个线程去负责同步写oplog,那么生产环境上,多少个集合是合理范围呢?如果当前线程负责的集合没有oplog需要同步了,她可以去处理其他集合的oplog吗(请原谅我现在还没来得及看源码)?

yunnotes

会有一次数据拷贝,这里使用队列,来协调拉oplog和重放oplog。使用mmap来实现,逻辑上会复杂不少,而且动态remap的开销也不小。关键是内存拷贝不是同步开销的大头。

yunnotes

直接把BlockQueue调大,如果问题在于重放太慢,把这个调大也没用

yunnotes

一批oplog分发到16个线程开始重放时,某个线程先干完了也不能帮其他线程干了;也可以实现成线程主动从队列里取任务,但这样就需要对队列加锁访问,MongoDB没有这么做。

yunnotes

一个集合只能由一个线程去同步,这个只适应于集合级别锁的mmapv1引擎,使用wiredtiger引擎,一个集合的oplog可分发到任意线程,根据文档id,而不是集合名称去分。

评论
2F
jakeruan

如果用户的集合个数很少(少于16个)或者很多(记得有一次在咱中文社区总微信群里,阿里DBA聊天说他们有12000+个集合),那不就慢成蜗牛了吗? 为何不参考MariaDB的“真正”并行复制呢?

yunnotes

集合很多的时候,即使集合级别锁的引擎也能很好的并行,不是很好么,怎么会慢?

集合很少的时候,如果使用mmapv1引擎,的确可能导致同步慢

现在MongoDB是串行拉oplog,并行复制,串行拉oplog目前来看,没必要并行,很少见到oplog拉得不够快的case,除非网卡已经到瓶颈了(这样的话,即使改成mariaDB那样的机制也解决不了问题)。

评论
3F
towells

producer thread、replBatcher thread 都是在secondary 上的线程吗?为什么要分两次处理?

关注
yunnotes
张友东,花名林青,阿里云数据库组技术专家,主要...
82篇文章|354关注
基于飞天分布式系统和高性能存储,提供三节点副本集的高可用架构,容灾切换,故障迁移完全透明化。并提供专业的数据库在...

提供海量、安全和高可靠的云存储服务。RESTful API的平台无关性,容量和处理能力的弹性扩展,按实际容量付费...

支持以数据库为核心的结构化存储产品之间的数据传输。 它是一种集数据迁移、数据订阅及数据实时同步于一体的数据传输服...

为您提供简单高效、处理能力可弹性伸缩的计算服务,帮助您快速构建更稳定、安全的应用,提升运维效率,降低 IT 成本...