数据库迁移这事儿,听起来像是运维的活儿,但真到了实战里,开发、架构、甚至业务方都得掺和一脚。尤其是MongoDB这种非关系型数据库,文档结构灵活,数据模型千变万化,迁移起来比MySQL那种规规矩矩的表结构要野得多。你没法靠一条ALTER TABLE搞定所有事,也没法指望导出个SQL文件就万事大吉。我见过太多团队,平时写代码风生水起,一到迁移就翻车——不是丢数据,就是业务中断,最惨的是迁移完才发现数据对不上,回滚又得折腾半天。所以今天这篇,咱就聊聊怎么把MongoDB整个库搬走,还能做到无缝切换、零丢失。

先泼盆冷水:别指望mongodump和mongorestore这对黄金搭档能解决所有问题。这俩工具确实好用,特别是小规模数据,几十个G以内,跑一遍就完事儿了。但你想想,如果数据量到了TB级别,或者业务对停机时间有硬性要求(比如金融交易、实时订单),那这套方案就露馅了。mongodump是逻辑备份,逐条读文档再写入,速度慢不说,还占用大量内存和磁盘I/O。更关键的是,它在备份过程中如果数据持续写入,那备份出来的数据可能是不一致的——你dump到一半,业务又插入了新数据,那这份备份就是个“残缺品”。所以,先把场景想清楚:数据量多大?停机窗口有多长?一致性要求有多高?这三个问题没想明白,后面全是坑。
那有人问了,既然mongodump不行,是不是得上专业工具?对,比如Percona的MongoDB备份工具,或者用MongoDB Atlas的在线迁移服务。但工具不是万能的,你真正要搞懂的是迁移的底层逻辑。整个迁移流程其实就三步:全量同步、增量追平、切换流量。全量同步就是把现有数据完整复制过去,这一步可以用mongodump加mongorestore,也可以用文件系统快照(比如LVM或EBS快照),后者速度更快,但要求源库和目标库在同一文件系统层面兼容。增量追平就复杂了——你得让目标库跟上源库在迁移期间产生的新写入。MongoDB的oplog(操作日志)就是干这个用的,它记录了所有写操作,你可以通过oplog同步工具(比如mongosync或自研脚本)把增量数据实时搬到目标库。最后一步切换流量,就是在目标库的数据追平到和源库只差几秒甚至几毫秒的时候,把应用连接串从源库切到目标库,完成无缝切换。
说到oplog,这里得展开讲一下。oplog是MongoDB副本集里特有的机制,它本质上是本地的一个capped collection(固定大小集合),存储了所有写操作的记录。你迁移的时候,全量同步完,目标库的数据状态对应源库的某个时间点T0。然后你启动增量同步,从T0往后读oplog,把后续所有写操作重放到目标库。这个过程要持续到源库和目标库的数据差距缩小到可接受范围。但有个坑:oplog的大小是有限的,默认可能是磁盘空间的5%。如果迁移期间写入量很大,oplog被覆盖了,那增量同步就断了,你只能重新做全量同步。所以迁移之前,最好检查一下源库oplog的保留时间,必要时临时调大oplog size,确保它能覆盖整个迁移周期。
再聊聊工具选型。如果你用的是MongoDB 4.2及以上版本,官方出了个mongosync工具,专门用于集群到集群的实时迁移。这东西比手动脚本靠谱多了,它能自动处理oplog追平、断点续传、校验数据一致性。但mongosync有个限制:它只支持源端和目标端都是副本集或分片集群,而且要求两端版本兼容。如果你是从自建环境迁到云上的MongoDB Atlas,那直接用Atlas的Live Migration功能就行,它会自动帮你搞定全量加增量,你只需要在业务低峰期切一下连接串。但如果你的环境比较特殊——比如源库是单节点(没有oplog),或者目标库版本比源库低——那就得做额外的兼容性处理,比如先把单节点转成副本集,或者升级目标库版本。
数据校验这步千万别省。迁移完成不等于成功,你得证明数据没丢、没多、没改。MongoDB的校验手段有两种:一种是对比文档数量,比如每个集合的count,但这个方法太粗糙,万一文档内容错了但数量对不上呢?另一种是采样对比,用mongoexport导出部分数据,再用脚本逐条对比字段值。如果数据量特别大,可以按分片键或时间范围抽样,至少覆盖每个分片和关键业务表。我见过一个案例,迁移完发现某个字段的类型从字符串变成了数字——因为源库用的是Python的pymongo,日期存成了字符串,目标库用Node.js驱动解析后自动转成了Date类型。这种坑,靠数量对比根本发现不了,必须做字段级别的校验。
切换流量的时候,别一把梭。建议分成两步走:先切读流量,再切写流量。具体做法是,在应用层配置多个MongoDB地址,把读请求指向目标库,写请求继续走源库。等观察一段时间(比如一个业务周期),确认读数据没问题了,再把写流量切过去。这个过程中,源库和目标库之间要保留增量同步,直到写流量切换完成、源库不再有业务写入为止。切换后也别急着关源库,至少保留一个业务周期(比如24小时或一个周末),以防需要回滚。回滚的场景是:如果目标库在写流量切换后出现性能问题或数据异常,你还能把写流量切回源库,因为源库的数据在这段时间内是完整保留的。
说点实战心得。第一,迁移前先做一次“预演”,在测试环境完整跑一遍流程,记录每一步的耗时和数据量变化,这样正式迁移时心里有数。第二,监控指标要盯紧:oplog的延迟、目标库的写入速率、源库的磁盘I/O,这些数据能帮你判断迁移是否健康。第三,别忽视网络带宽——如果源库和目标库跨机房,或者跨云厂商,带宽不够会导致增量追平速度赶不上业务写入速度,迁移永远结束不了。我之前帮一个客户迁移,源库在阿里云,目标库在AWS,业务高峰期每秒写入几千条,结果带宽只有50Mbps,增量同步延迟越来越大,只能限流业务写入,才勉强追平。所以,迁移前评估带宽,必要时临时扩容,或者选在业务低峰期操作。
说到底,MongoDB全库迁移不是一场赌博,而是一场有预案的战役。你把全量同步、增量追平、流量切换这三步走扎实,把oplog、数据校验、回滚方案都提前想清楚,那“无缝切换零丢失”就不是口号,而是可以落地的目标。我见过太多团队栽在“以为很简单”上——结果不是丢数据就是业务中断。但反过来,只要按这套方法论走,哪怕数据量再大、业务再敏感,你也能做到从容不迫。技术这事儿,从来都是细节决定成败。


