MongoDB Secondary同步慢问题分析

GiuseppeBro 8年前
   <h2>问题背景</h2>    <p>最近生产环境出现多次Primary写入QPS太高,导致Seconary的同步无法跟上的问题(Secondary上的最新oplog时间戳比Primary上最旧oplog时间戳小),使得Secondary变成RECOVERING状态,这时需要人工介入处理,向Secondary发送resync命令,让Secondary重新全量同步一次。</p>    <h2>同步过程</h2>    <p>下图是MongoDB数据同步的流程</p>    <p><img src="https://simg.open-open.com/show/fef0ac3f643298153cfc9cb9e1d91195.jpg"></p>    <p>Primary上的写入会记录oplog,存储到一个固定大小的 <a href="/misc/goto?guid=4959673679469543436" rel="nofollow,noindex">capped collection</a> 里,Secondary主动从Primary上拉取oplog并重放应用到自身,以保持数据与Primary节点上一致。</p>    <h3>initial sync</h3>    <p>新节点加入(或者主动向Secondary发送resync)时,Secondary会先进行一次initial sync,即全量同步,遍历Primary上的所有DB的所有集合,将数据拷贝到自身节点,然后读取『全量同步开始到结束时间段内』的oplog并重放。全量同步不是本文讨论的重点,将不作过多的介绍。</p>    <h3>tailing oplog</h3>    <p>全量同步结束后,Secondary就开始从结束时间点建立tailable cursor,不断的从同步源拉取oplog并重放应用到自身,这个过程并不是由一个线程来完成的,mongodb为了提升同步效率,将拉取oplog以及重放oplog分到了不同的线程来执行。</p>    <ul>     <li>producer thread,这个线程不断的从同步源上拉取oplog,并加入到一个BlockQueue的队列里保存着,BlockQueue最大存储240MB的oplog数据,当超过这个阈值时,就必须等到oplog被replBatcher消费掉才能继续拉取。</li>     <li>replBatcher thread,这个线程负责逐个从producer thread的队列里取出oplog,并放到自己维护的队列里,这个队列最多允许5000个元素,并且元素总大小不超过512MB,当队列满了时,就需要等待oplogApplication消费掉。</li>     <li>oplogApplication会取出replBatch thread当前队列的所有元素,并将元素根据docId(如果存储引擎不支持文档锁,则根据集合名称)分散到不同的replWriter线程,replWriter线程将所有的oplog应用到自身;等待所有oplog都应用完毕,oplogApplication线程将所有的oplog顺序写入到local.oplog.rs集合。</li>    </ul>    <p>producer的buffer和apply线程的统计信息都可以通过db.serverStatus().metrics.repl来查询到,在测试过程中,向Primary模拟约10000 qps的写入,观察Secondary上的同步,写入速率远小于Primary,大致只有3000左右的qps,同时观察到 producer的buffer很快就达到饱和,可以判断出oplog重放的线程跟不上 。</p>    <p>默认情况下,Secondary采用16个replWriter线程来重放oplog,可通过启动时设置replWriterThreadCount参数来定制线程数,当提升线程数到32时,同步的情况大大改观,主备写入的qps基本持平,主备上数据同步的延时控制在1s以内,进一步验证了上述结论。</p>    <h2>改进思路</h2>    <p>如果因Primary上的写入qps很高,经常出现Secondary同步无法追上的问题,可以考虑以下改进思路</p>    <ul>     <li>配置更高的replWriterThreadCount,Secondary上加速oplog重放,代价是更高的内存开销</li>     <li>使用更大的oplog,可按照官方教程 <a href="/misc/goto?guid=4959673679551786655" rel="nofollow,noindex">修改oplog的大小</a> , <a href="/misc/goto?guid=4959673679650101680" rel="nofollow,noindex">阿里云MongoDB数据库</a> 增加了patch,能做到在线修改oplog的大小。</li>     <li>将writeOpsToOplog步骤分散到多个replWriter线程来并发执行,这个是官方目前在考虑的策略之一,参考 <a href="/misc/goto?guid=4959673679722672598" rel="nofollow,noindex">Secondaries unable to keep up with primary under WiredTiger</a></li>    </ul>    <h2>参考资料</h2>    <ul>     <li><a href="/misc/goto?guid=4959673679469543436" rel="nofollow,noindex">capped collection</a></li>     <li><a href="/misc/goto?guid=4959673679722672598" rel="nofollow,noindex">SERVER-18908</a></li>     <li><a href="/misc/goto?guid=4959673679551786655" rel="nofollow,noindex">修改oplog的大小</a></li>     <li><a href="/misc/goto?guid=4959673679650101680" rel="nofollow,noindex">阿里云MongoDB数据库</a></li>    </ul>    <p> </p>    <p>来自: <a href="/misc/goto?guid=4959673679848620136" rel="nofollow">http://blog.yunnotes.net/index.php/mongodb-scondary-cannot-catchup/</a></p>    <p> </p>