聊聊 MongoDB 数据同步:一次完整复盘,4 条路线和我踩过的坑
前几天被拉进一个项目,需求挺直白:订单数据在 MongoDB 里,要实时同步一份到数仓做报表,再往国产关系型库里备一份。
我心想,这活儿我熟。结果真干起来,还是踩了几个坑。今天把这次完整复盘记下来,给同样要碰「MongoDB 数据同步」的同行提个醒。
先说说背景
源端是一个三节点的 MongoDB 副本集,订单数据量不小,天天有写入。目标端是两部分:一个数仓(要增量),一个国产关系型库(要长期实时同步)。
接到需求我第一件事,是把「同步」这俩字先掰开——副本集内部的复制和跨库的异构同步,根本是两码事。这个判断决定了后面所有选择。
第一件事:副本集扩容
项目一上来要加一个从节点做读写分离。这个最简单,rs.add() 一下就行:
rs.add("mongodb://新节点IP:27017")
加进去之后,新节点进 STARTUP2 状态开始初始同步。我习惯再开个窗口盯着:
db.currentOp({ "desc": { $regex: "initial sync|repl" } })
坑就出在这:我图省事,没先评估 oplog 大小。结果数据量大、同步到一半,从节点追着追着变 stale 了,日志里就一句 too stale to catch up。没办法,删数据重来,这次老老实实把 oplog 调大了才过。
教训:扩容前先算 oplog 窗口,别等它报错。
第二件事:往数仓导增量
数仓那边要增量,我第一个想到 Change Streams,毕竟官方的东西。写了个 Node.js 消费者挂着收变化,跑得挺好。
然后问题来了:半夜消费者进程挂了,重启后 resumeToken 没持久化好,直接从当前时间点重放,中间漏了一段。等业务发现数据对不上,已经过去大半天。
教训:Change Streams 是半成品,断点得你自己扛。 token 必须落盘、随进度更新,别图省事存内存里。
第三件事:往国产库做实时同步
这一步最纠结。目标端是国产关系型库,前几条路线都差点意思:
- Change Streams 要自己写映射、管断点,维护成本高;
- MongoShake 主要解决 Mongo→Mongo、Mongo→Kafka,到关系型库还得再接一层;
- 云 DTS 绑云,出不了圈。
最后选了异构 CDC 工具,用的是金仓的 KFS(Kingbase FlySync)。它走的路子和 Oracle GoldenGate 是一个思路:挂在源库上解析日志(Redo Log、Binlog、MongoDB 的 oplog),把变化捕获下来,翻译成目标端能懂的语句写进去。内置 20 余种源库适配器,MongoDB、Oracle、MySQL 都在里面。
用下来几个印象深的点:延迟是秒级的(日志级 CDC,不是轮询);断点续传,任务断了按位点接着跑;还带在线数据比对,对不上会告警,省了我自己写校验脚本的功夫。
当然得说句实在的:这玩意儿要部署管理端、要付费,比自研贵。但换来的是不用养一套同步代码,长期看划算。
复盘:这次踩过的坑
- oplog 窗口没提前算,从节点 stale,重来一遍;
- resumeToken 没持久化,漏数据半天才发现;
- 同步完没校验,差点把「跑完」当「跑对」;
- 账号权限没配好,工具一开始报
not authorized,折腾半天才发现是读 oplog 的权限没给。
总结
这次下来最大的体会:MongoDB 数据同步不难,难在把「该选哪条路线」和「延迟、断点、一致性」三件事提前想清楚。 目标端是谁,决定了你该走哪条路;其余三件事,决定了你半夜会不会被叫醒。
写出来,希望你的生产环境用不上这些坑。
我是DBA小马哥,十年一线数据库运维。写的东西都是生产环境里趟出来的,关注我,少踩坑。
浙公网安备 33010602011771号