前阵子帮一个朋友搞MongoDB迁移,他那边数据量大概2TB,业务还不能停。折腾了两天两夜,踩了不少坑,也总结出一些靠谱的方法。今天就把这些实战经验掰开揉碎了聊聊,。

先搞清楚一个核心问题:为什么要迁移MongoDB?很多人觉得跟MySQL一样,导出导入就完事了。但MongoDB的文档模型、索引设计、分片策略都跟关系型数据库不一样。比如你从自建机房迁到云上,或者从旧版本升到新版本,再或者从副本集改成分片集群,每个场景的迁移策略都差很多。我遇到过最坑的是直接拿mysqldump的思路用mongodump,结果索引重建花了三个小时。所以第一步,先画清楚你的迁移地图:源端和目标端的版本、集群架构、数据量级、业务容忍度,这些信息缺一不可。
说到工具选择,mongodump和mongorestore是官方标配,但别迷信它。对于小规模数据(几十GB以内),这俩工具确实够用。但一旦到了TB级别,你就会发现导出速度慢、网络带宽浪费大、断点续传基本靠猜。更推荐用的是mongoexport和mongoimport的组合,或者直接用云厂商的迁移服务。我做过一个对比测试:同样100GB数据,mongodump用了4小时,而用mongoexport加上--type csv参数只用了1.5小时,因为纯文本格式省去了BSON的序列化开销。不过要是你的数据里有嵌套文档,mongoexport会把它展平,这时候就得用mongodump了。
实时迁移才是真正的硬骨头。业务不能停,数据还在不断写入,怎么保证一致性?传统做法是先停写、再导出、再导入,但这对于24小时在线的业务来说等于自杀。靠谱的路线是:先用mongorestore做全量迁移,同时开启oplog同步。MongoDB的oplog记录了所有写操作,类似于MySQL的binlog。你可以在源端部署一个隐藏节点,只用来收集oplog,然后通过mongosync工具把增量变更实时推给目标端。等数据追平后,业务切流,整个过程业务几乎无感知。这个方案我跑了三个月,数据延迟最多不超过5秒。
索引迁移这块很容易翻车。很多人直接先导数据再建索引,结果数据导完发现建索引要跑一整天。正确的姿势是:在迁移前,先把源端的索引定义导出来,在目标端提前建好。这样数据导入的时候,索引就能实时维护,避免事后重建的巨量IO消耗。另外要注意,MongoDB的索引名在副本集内是唯一的,但跨集群迁移时不会自动去重。我吃过一次亏:源端有两个同名索引但字段不同,迁移后目标端直接报错。所以迁移前一定要用db.collection.getIndexes()仔细检查,手动清理或重名冲突的索引。
数据一致性校验是一道防线,但很多人直接跳过。你以为mongorestore成功了就万事大吉?实际上,网络丢包、磁盘坏道、版本兼容性问题都可能导致数据丢失或损坏。推荐用dbHash或mongocheck做校验。具体做法是:在源端和目标端分别对每个集合执行db.collection.hashData(),然后逐条比对哈希值。如果有差异,再用db.collection.find().sort({_id:1}).limit(100)抽样检查。我自己踩过一次坑:迁移后业务报错,查了三天才发现是某个文档里嵌的数组元素顺序变了,而哈希校验根本没发现——因为顺序变化不影响哈希值。所以还得加上文档级校验,用--documents参数逐文档对比。
版本升级迁移是个隐藏的坑。MongoDB的存储引擎在3.2版本从MMAPv1切换到WiredTiger,如果你的源端是3.0以下版本,直接迁移到4.0以上版本,可能会出现数据类型不兼容的问题。比如MMAPv1里的某些字段长度限制、索引类型,在WiredTiger下会直接报错。我建议的做法是:分两步走,先升级到兼容版本(比如3.6),再迁移到目标版本(比如6.0)。中间用mongodump和mongorestore做过渡,避免直接跨版本迁移。另外,注意检查你们的业务代码里有没有用到过时的API,比如$where操作符、group命令这些,在新版本里可能已经被标记为废弃。
聊点实战技巧。迁移过程中,网络带宽是最大瓶颈。建议在迁移前用iperf测试源端和目标端之间的实际带宽,如果低于100Mbps,那就得考虑用压缩传输。mongodump自带了--gzip参数,能压缩到原来的三分之一左右,但会消耗CPU。另一个技巧是分片迁移:如果你的MongoDB已经用了分片集群,别傻乎乎地整个集群导出。每个分片单独迁移,利用分片键做范围切割,可以并行迁移,速度翻好几倍。还有个小细节:迁移前务必关闭目标端的balancer,否则数据均衡和导入会互相打架,导致性能急剧下降。
数据迁移不是一次性动作,而是一个持续优化的过程。迁移完成后,建议运行至少一周的灰度验证:把10%的读流量切到新库,观察业务指标和数据库性能。如果一切正常,再逐步切流。万一出问题,还有回滚的余地。我见过最惨的案例是某公司直接全量切流,结果新库性能差了三倍,业务瘫痪两小时。所以,永远给自己留一条退路。
写这篇文章的时候,我脑子里一直回响着朋友那句“原来迁移这么麻烦”。确实,MongoDB迁移看着像数据搬家,实际上涉及到版本兼容、索引策略、增量同步、一致性校验、性能调优等多个维度。但只要按上述步骤走,把每个环节的坑提前填上,数据无缝对接就不是什么难事。毕竟,数据库迁移这件事,做得越好,越没人注意到它的存在。


