MongoDB Secondary同步慢问题分析(续)

本文涉及的产品
云原生多模数据库 Lindorm,多引擎 多规格 0-4节点
云数据库 Redis 版,社区版 2GB
推荐场景:
搭建游戏排行榜
云数据库 MongoDB,通用型 2核4GB
简介: 在MongoDB Scondary同步慢问题分析文中介绍了因Primary上写入qps过大,导致Secondary节点的同步无法追上的问题,本文再分享一个case,因oplog的写入被放大,导致同步追不上的问题。 MongoDB用于同步的oplog具有一个重要的『幂等』特性,也就是说,一条oplo

MongoDB Scondary同步慢问题分析文中介绍了因Primary上写入qps过大,导致Secondary节点的同步无法追上的问题,本文再分享一个case,因oplog的写入被放大,导致同步追不上的问题。

MongoDB用于同步的oplog具有一个重要的『幂等』特性,也就是说,一条oplog在备上重放多次,得到的结果跟重放一次结果是一样的,这个特性简化了同步的实现,Secondary不需要有专门的逻辑去保证一条oplog在备上『必须仅能重放』一次。

为了保证幂等性,记录oplog时,通常需要对写入的请求做一下转换,举个例子,某文档x字段当前值为100,用户向Primary发送一条{$inc: {x: 1}},记录oplog时会转化为一条{$set: {x: 101}的操作,才能保证幂等性。

幂等性的代价

简单元素的操作,$inc 转化为 $set并没有什么影响,执行开销上也差不多,但当遇到数组元素操作时,情况就不一样了。

当前文档内容

mongo-9551:PRIMARY> db.coll.find()
{ "_id" : 1, "x" : [ 1, 2, 3 ] }

在数组尾部push 2个元素,查看oplog发现$push操作被转换为了$set操作(设置数组指定位置的元素为某个值)。


mongo-9551:PRIMARY> db.coll.update({_id: 1}, {$push: {x: { $each: [4, 5] }}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
mongo-9551:PRIMARY> db.coll.find()
{ "_id" : 1, "x" : [ 1, 2, 3, 4, 5 ] }
mongo-9551:PRIMARY> use local
switched to db local
mongo-9551:PRIMARY> db.oplog.rs.find().sort({$natural: -1}).limit(1)
{ "ts" : Timestamp(1464081601, 1), "h" : NumberLong("7793405363406192063"), "v" : 2, "op" : "u", "ns" : "test.coll", "o2" : { "_id" : 1 }, "o" : { "$set" : { "x.3" : 4, "x.4" : 5 } } }

$push转换为带具体位置的$set开销上也差不多,但接下来再看看往数组的头部添加2个元素

mongo-9551:PRIMARY> db.coll.update({_id: 1}, {$push: {x: { $each: [6, 7], $position: 0 }}})
WriteResult({ "nMatched" : 1, "nUpserted" : 0, "nModified" : 1 })
mongo-9551:PRIMARY> db.coll.find()
{ "_id" : 1, "x" : [ 6, 7, 1, 2, 3, 4, 5 ] }
mongo-9551:PRIMARY> use local
switched to db local
mongo-9551:PRIMARY> db.oplog.rs.find().sort({$natural: -1}).limit(1)
{ "ts" : Timestamp(1464082056, 1), "h" : NumberLong("6563273714951530720"), "v" : 2, "op" : "u", "ns" : "test.coll", "o2" : { "_id" : 1 }, "o" : { "$set" : { "x" : [ 6, 7, 1, 2, 3, 4, 5 ] } } }

可以发现,当向数组的头部添加元素时,oplog里的$set操作不再是设置数组某个位置的值(因为基本所有的元素位置都调整了),而是$set数组最终的结果,即整个数组的内容都要写入oplog。当push操作指定了$slice或者$sort参数时,oplog的记录方式也是一样的,会将整个数组的内容作为$set的参数。

$pull, $addToSet等更新操作符也是类似,更新数组后,oplog里会转换成$set数组的最终内容,才能保证幂等性。

案例分析

当数组非常大时,对数组的一个小更新,可能就需要把整个数组的内容记录到oplog里,我们遇到一个实际的生产环境案例,用户的文档内包含一个很大的数组字段,1000个元素总大小在64KB左右,这个数组里的元素按时间反序存储,新插入的元素会放到数组的最前面($position: 0),然后保留数组的前1000个元素($slice: 1000)。

上述场景导致,Primary上的每次往数组里插入一个新元素(请求大概几百字节),oplog里就要记录整个数组的内容,Secondary同步时会拉取oplog并重放,『Primary到Secondary同步oplog』的流量是『客户端到Primary网络流量』的上百倍,导致主备间网卡流量跑满,而且由于oplog的量太大,旧的内容很快被删除掉,最终导致Secondary追不上,转换为RECOVERING状态。

MongoDB对json的操作支持很强大,尤其是对数组的支持,但在文档里使用数组时,一定得注意上述问题,避免数组的更新导致同步开销被无限放大的问题。使用数组时,尽量注意

  1. 数组的元素个数不要太多,总的大小也不要太大
  2. 尽量避免对数组进行更新操作
  3. 如果一定要更新,尽量只在尾部插入元素,复杂的逻辑可以考虑在业务层面上来支持

比如上述场景,有如下的改进思路

  1. 将数组的内容放到单独的集合存储,将数组的操作转化为对集合的操作(capped collection能很好的支持$slice的功能)
  2. 如果一定要用数组,插入数组元素时,直接放到尾部,让记录就是按时间戳升序存储,在使用时反向遍历({$natural: -1})取最新的元素。保持最近1000条的功能,则可在业务逻辑里实现掉,比如增加后台任务来检测,当数组元素超过某个阈值如2000时,就将数组截断到1000条。

再说同步

MongoDB Scondary同步慢问题分析我介绍了通过修改Secondary上重放oplog的线程数来提升备的同步能力的方法。但其实对于MongoDB的同步,并没有一种配置,能完美的解决所有同步场景,Primary上的workload不同,主备间同步的状况也会不同。

为了尽量避免出现Secondary追不上的场景,需要注意以下几点

  1. 保证Primary节点有充足的服务能力,如果用户的请求就能把Primary的资源跑得很满,那么势必会影响到主备同步。
  2. 合理配置oplog的大小,可以结合写入的情况,预估下oplog的大小,比如oplog能存储一天的写入量,这样即使备同步慢、故障、或者临时下线维护等,只要不超过1天,恢复后还是有希望继续同步的。
  3. 尽量避免复杂的数组更新操作,尽量避免慢更新(比如更新的查询条件需要遍历整个集合)

参考资料

相关实践学习
MongoDB数据库入门
MongoDB数据库入门实验。
快速掌握 MongoDB 数据库
本课程主要讲解MongoDB数据库的基本知识,包括MongoDB数据库的安装、配置、服务的启动、数据的CRUD操作函数使用、MongoDB索引的使用(唯一索引、地理索引、过期索引、全文索引等)、MapReduce操作实现、用户管理、Java对MongoDB的操作支持(基于2.x驱动与3.x驱动的完全讲解)。 通过学习此课程,读者将具备MongoDB数据库的开发能力,并且能够使用MongoDB进行项目开发。   相关的阿里云产品:云数据库 MongoDB版 云数据库MongoDB版支持ReplicaSet和Sharding两种部署架构,具备安全审计,时间点备份等多项企业能力。在互联网、物联网、游戏、金融等领域被广泛采用。 云数据库MongoDB版(ApsaraDB for MongoDB)完全兼容MongoDB协议,基于飞天分布式系统和高可靠存储引擎,提供多节点高可用架构、弹性扩容、容灾、备份回滚、性能优化等解决方案。 产品详情: https://www.aliyun.com/product/mongodb
相关文章
|
NoSQL MongoDB 索引
MongoDB副本集同步原理
MongoDB的同步原理,官方文档介绍的比较少,网上资料也不是太多,下面是结合官方文档、网上资料和测试时候的日志,整理出来的一点东西。
3333 0
|
4月前
|
SQL 分布式计算 NoSQL
快速实践: 通过 Flink CDC 一键整库同步 MongoDB 到 Paimon
Apache Paimon (incubating) 是一项流式数据湖存储技术,可以为用户提供高吞吐、低延迟的数据摄入、流式订阅以及实时查询能力。
76542 4
快速实践: 通过 Flink CDC 一键整库同步 MongoDB 到 Paimon
|
8月前
|
数据库 索引
MongoDB-复制集同步规则
初始化同步 • 将一个新的节点加入到复制集中时, 就需要进行初始化同步 • 初始化同步会先清空自己所有的内容, 保证将来自己和主节点一模一样 • 初始化同步会将主节点中现有所有的 ‘数据库’, ‘集合’, ‘文档’, ‘索引’ 全部拷贝过来 • 但是在拷贝的过程中主节点仍然可能会做一些其它操作, 新增一些其它的数据等
54 0
|
NoSQL MongoDB 数据库
elasticsearch同步mongodb--mongo connector的使用
elasticsearch同步mongodb--mongo connector的使用
206 0
elasticsearch同步mongodb--mongo connector的使用
|
存储 JSON NoSQL
插入大量数据至MongoDB数据库的速度问题分析
插入大量数据至MongoDB数据库的速度问题分析
|
JSON 分布式计算 DataWorks
|
JSON 分布式计算 DataWorks
|
SQL JSON 分布式计算
|
SQL 分布式计算 DataWorks
DataWorks百问百答28:MongoDB时间戳类型字段如何实现增量同步?
结合赋值节点通过MongoDB时间戳类型字段实现增量同步场景示例
2236 0
DataWorks百问百答28:MongoDB时间戳类型字段如何实现增量同步?
|
NoSQL 数据库
【直播回顾】阿里高级开发工程师烛昭:Redis&MongoDB的同步、迁移以及混合云场景构建
本次直播将主要介绍Redis和MongoDB的同步和迁移,进一步探索如何满足用户容灾和多活的需求,以及如何灵活构建混合云场景。
2620 0

相关产品

  • 云数据库 MongoDB 版