技术分享 | 一个案例总结 MongoDB 与 Redis 主从同步问题


外向笑小鸭子
外向笑小鸭子 2024-01-02 11:15:23 51346
分类专栏: 资讯

1问题背景

现场 MongoDB 版本为 4.4.14,PSS 架构。发现一个从节点的状态不正常,一直处于 STARTUP2 的状态,查看 optime 是 1970,因此无法同步数据也无法提供服务。现场同事将另一台从节点的 data 直接暴力 scp 到了不正常的那台,data 中包含的 oplog 也同步过去了,但同步完重启后,不正常的那台还是在 STARTUP2,optime 也还是 1970。

2问题分析

了解现场的情况,直接 scp 从节点的 data 会导致数据不一致,这种情况下同步异常是符合预期的,现场数据量不大(20G 不到),因此可以通过逻辑初始化方式进行主从修复。

让现场同事进行如下操作:先 kill 异常从实例,然后把 data 目录清空,最后重新启动实例。做完这些,告诉现场同事只需要耐心等待就可以了,因为数据量不大,理论上一段时间后即可恢复同步正常,但是结局啪啪打脸了,一段时间后现场同事反馈还是同步失败。

此时只能通过查看日志进行解决了,让现场同事提供对应时间段的 mongo 日志,通过查看日志,发现如下信息:

{"t":{"$date":"2023-08-22T19:01:15.574+08:00"},"s":"I",  "c":"INITSYNC", "id":21192,   "ctx":"ReplCoordExtern-10","msg":"Initial sync status and statistics","attr":{"status":"failed","statistics":{"failedInitialSyncAttempts":10,"maxFailedInitialSyncAttempts":10,"initialSyncStart":{"$date":"2023-08-22T09:51:56.710Z"},"totalInitialSyncElapsedMillis":4158864,"initialSyncAttempts":[{"durationMillis":418787,"status":"OplogStartMissing: error fetching oplog during initial sync :: caused by :: Our last optime fetched: { ts: Timestamp(1692698292, 26), t: 44 }. source's GTE: { ts: Timestamp(1692698294, 42), t: 44 }","syncSource":"master:27017","rollBackId":8,"operationsRetried":0,"totalTimeUnreachableMillis":0},{"durationMillis":409866,"status":"OplogStartMissing: error fetching oplog during initial sync :: caused by :: Our last optime fetched: { ts: Timestamp(1692698692, 62), t: 44 }. source's GTE: { ts: Timestamp(1692698709, 12), t: 44 }","syncSource":"master:27017","rollBackId":8,"operationsRetried":0,"totalTimeUnreachableMillis":0},{"durationMillis":429278,"status":"OplogStartMissing: error fetching oplog during initial sync :: caused by :: Our last optime fetched: { ts: Timestamp(1692699126, 26), t: 44 }. source's GTE: { ts: Timestamp(1692699137, 18), t: 44 }","syncSource":"master:27017","rollBackId":8,"operationsRetried":0,"totalTimeUnreachableMillis":0},{"durationMillis":427610,"status":"OplogStartMissing: error fetching oplog during initial sync :: caused by :: Our last optime fetched: { ts: Timestamp(1692699549, 44), t: 44 }. source's GTE: { ts: Timestamp(1692699570, 87), t: 44 }",

通过上面信息,可以定位问题。从库从主库读取 oplog 位点时,主库目前的最小位点大于从库所需要读取的位点,因此导致同步失败,一般这种情况有两种可能:

  1. 全量数据数据量较大,同步时间较长,导致 oplog 之前的位点被覆盖。
  2. 该段时间内的修改操作较多,导致 oplog 之前的位点被覆盖。

3问题处理

无论是上面哪种情况,都可以通过调整 oplog 大小进行解决。让现场同事先临时调大一倍 oplog 的值,然后重新进行同步,一段时间后现场同事反馈主从同步已经恢复。实际生产中 oplog 大小的设置参考[1]

4问题思考

问题解决了,但是透过这个小案例,我们是否能收获更多呢?答案是肯定的。

思考如下几个问题:

问题一:除了通过逻辑初始化方式进行主从修复,是否还有其他方式呢?

问题二:MongoDB 使用逻辑初始化方式进行主从修复,与 Redis 的主从修复相比有何异同?

问题一

对于问题一,常见的我们可以选择以下三种方式:

  1. 通过关闭异常实例,清空数据目录,然后启动 mongo,mongo 会自动进行初始化,从主实例同步数据,即 case 中使用的方式,该种方式操作简单,最安全,推荐

  2. 对集群中正常节点进行 LVM 快照,然后复制数据到异常实例,启动异常实例。该方式[2] 也不会阻塞源端读写,操作相对第一种方式稍显复杂。

  3. 对集群中正常节点执行 db.fsyncLock() 操作加锁后通过 cp 或 scp 等方式将数据目录拷贝到异常实例,然后对正常实例执行 db.fsyncUnlock() 操作释放锁,然后启动异常实例。该操作会阻塞源端写操作,不推荐。

问题二

对于 MongoDB 使用逻辑初始化方式和 Redis 进行主从修复时,都是分为 全量数据 + 增量同步 两个阶段。

第一阶段

MongoDB 会克隆除 local 数据库之外的所有数据库。为了进行克隆,mongod 扫描每个源数据库中的各个集合,并将所有数据插入到这些集合各自的副本中。当初始化同步完成后,目标成员会从 STARTUP2 状态转为 SECONDARY 状态。

Redis 从实例会连接主实例,发送 psync 或者 sync 命令(不同版本有差别),主实例收到命令后,会通过执行 bgsave 命令生成 RDB 快照文件。

第二阶段

MongoDB 从节点成员在初始化同步之后会获取在数据复制期间新增的 oplog 记录,oplog 是循环写的,如果过小,可能会被覆盖,从而导致同步失败。oplog 大小可以通过 db.getReplicationInfo() 命令查看。

mgset-919:PRIMARY>db .getReplicationInfo()
{
  "logSizeMB" : 512,
  "usedMB" : 0.01,
  "timeDiff" : 228185,
  "timeDiffHours" : 63.38,
  "tFirst" :"Thu Jul 27 2023 16:45:11 GMT+0800(CST)",
  "tLast" :"sun Jul 30 2023 08:08:16 GMT+0800(CST)",
  "now":"Tue Aug 29 2023 16:44:53 GMT+0800 (CST)"
  ……

可通过 db.adminCommand({replSetResizeOplog: 1, size: (200* 1024)}) 命令在线修改,单位为 MB。

Redis 主实例在 bgsave 执行完成后,会向从实例发送快照文件。在此期间写操作命令会记录在缓冲区内,对应的参数是 client-output-buffer-limit,表示大小限制是 512M,持续性限制是当客户端缓冲区大小持续 120 秒超过 128M,则关闭 SLAVE 客户端连接。如果设置过小,也会导致同步失败。可以通过 config get client-output-buffer-limit 命令查看。

#redis-cli -h 127.0.0.1 -p xxxx
127.0.0.1:xxxx> config get client-output-buffer-limit
1) "client-output-buffer-limit"
2) "noraml 524288000 0 0 slave 536870912 134217728 120 pubsub 33554432 8388608 60"

可通过 config set client-output-buffer-limit ”slave 1073741824 268435456 120" 在线设置,单位为比特。

PS:Redis 从 2.8 版本开始支持断点续传。该技术依赖增量复制缓冲区,称为 repl_backlog_buffer,是一个定长的环形数组。如果数组内容写满了,则会从头开始覆盖之前的内容,未被覆盖的情况下,从节点与主节点能够在网络连接断开重连后,只从中断处继续进行复制,而不必重新同步。对应的参数为 repl-backlog-size,如果网络环境较差,可以适当增大该参数值。

5问题总结

通过这个 case,我们将 MongoDB 和 Redis 主从复制原理、修复方式以及常见问题完整的串联起来了,是不是收获不小呢?

6有奖问答

问题:client-output-buffer-limit 对应的缓冲区和 repl-backlog-size 对应的缓冲区有啥区别呢?

有答案的朋友,欢迎在文末留言。作者会参与讨论并在文章发出 24 小时之后选出一位优质回答读者送出社区周边一份(二选一)~

参考资料

[1]

oplog 值参考: https://mongoing.com/blog/oplog-size

[2]

快照备份: https://www.mongodb.com/docs/v4.4/tutorial/backup-with-filesystem-snapshots/

 

网站声明:如果转载,请联系本站管理员。否则一切后果自行承担。

本文链接:https://www.xckfsq.com/news/show.html?id=34129
赞同 0
评论 0 条
外向笑小鸭子L0
粉丝 0 发表 622 + 关注 私信
上周热门
如何使用 StarRocks 管理和优化数据湖中的数据?  2947
【软件正版化】软件正版化工作要点  2868
统信UOS试玩黑神话:悟空  2828
信刻光盘安全隔离与信息交换系统  2723
镜舟科技与中启乘数科技达成战略合作,共筑数据服务新生态  1256
grub引导程序无法找到指定设备和分区  1221
华为全联接大会2024丨软通动力分论坛精彩议程抢先看!  165
2024海洋能源产业融合发展论坛暨博览会同期活动-海洋能源与数字化智能化论坛成功举办  163
点击报名 | 京东2025校招进校行程预告  163
华为纯血鸿蒙正式版9月底见!但Mate 70的内情还得接着挖...  158
本周热议
我的信创开放社区兼职赚钱历程 40
今天你签到了吗? 27
如何玩转信创开放社区—从小白进阶到专家 15
信创开放社区邀请他人注册的具体步骤如下 15
方德桌面操作系统 14
用抖音玩法闯信创开放社区——用平台宣传企业产品服务 13
我有15积分有什么用? 13
如何让你先人一步获得悬赏问题信息?(创作者必看) 12
2024中国信创产业发展大会暨中国信息科技创新与应用博览会 9
中央国家机关政府采购中心:应当将CPU、操作系统符合安全可靠测评要求纳入采购需求 8

加入交流群

请使用微信扫一扫!