☰
单实例 MongoDB 数据同步实战:mongo shake 增量同步与配置指南
2026/10/22 8:33:42 网站建设 项目流程

1. 为什么单实例 MongoDB 之间也需要数据同步

很多人一听到“数据同步”,第一反应就是副本集、分片集群、跨机房容灾这些大词。但实际工作中,我遇到最多的场景反而是:两台完全独立的单实例 MongoDB 之间需要把数据搬来搬去。比如开发环境要定期从测试环境拉一份最新数据下来做调试,比如两个业务系统各自维护一个单实例 MongoDB,但某些基础数据需要保持一致,再比如线上出了数据问题,需要把生产环境某个库快速复制到一台临时机器上做分析。

这些场景有几个共同特点:数据量不算特别大,但表结构复杂;同步频率不高,可能一天一次甚至一周一次;两边都是单实例,没有副本集那套 oplog 机制可以用。这时候你如果去搭一个副本集,或者上 Change Streams,成本就太高了。而 mongodump + mongorestore 虽然能用,但它是全量覆盖,没法做增量,而且对业务库的集合结构有要求,遇到一些特殊类型的数据还容易出问题。

mongo shake这个工具就是冲着这类需求来的。它本质上是一个 MongoDB 数据同步工具,支持在单实例之间做集合级别的数据搬运,可以全量也可以增量,配置简单,不需要两边组成集群。我第一次用它是在一个内部数据迁移项目里,当时要把一个业务库的十几个集合同步到另一台机器上,试了好几种方案,最后用 mongo shake 跑通了,整个过程比想象中省事。

这篇文章我会从实际使用角度出发,把 mongo shake 在单实例 MongoDB 之间做数据同步的完整流程拆开讲。包括它适合什么场景、核心原理是什么、怎么配置、怎么跑、遇到问题怎么排查。如果你手头正好有两台独立的 MongoDB 需要同步数据,或者你正在找一个比 mongodump 更灵活的方案,那这篇内容应该能帮你省不少时间。

2. mongo shake 的核心机制与适用边界

2.1 它到底是怎么搬数据的

mongo shake 的工作方式可以理解成“读一端、写另一端”的管道模型。它会在源端 MongoDB 上建立连接,按照你配置的规则读取集合里的文档,然后在目标端 MongoDB 上执行写入操作。整个过程不依赖副本集的 oplog,也不需要在源端开启任何特殊模式,只要两边网络能通、账号有权限就行。

具体来说,它内部维护了一个同步任务的概念。每个任务对应一个源集合到一个目标集合的映射关系。任务启动后,它会先做一次全量扫描,把源集合里符合条件的文档全部读出来写到目标端。全量完成后,如果配置了增量模式,它会记录一个时间戳或者某个递增字段的位置,后续按这个位置继续拉取新产生的数据。

这里有个关键点:mongo shake 的增量同步不是基于 oplog 的,而是基于你指定的“增量字段”。这个字段通常是一个时间字段或者自增 ID。它每次同步完会记住这个字段的最大值,下次从这个值之后继续读。所以它适合的是那种有明确递增标识的数据,比如订单表有创建时间、日志表有自增序号。如果数据本身没有这种字段,那就只能做全量同步,每次覆盖目标端。

2.2 和 mongodump/mongorestore 的区别在哪

很多人会问,既然都是搬数据,为什么不用官方的 mongodump 和 mongorestore?我一开始也是这么想的,后来在实际项目里对比了一下,发现几个明显的差异。

mongodump 是物理级别的备份工具,它导出的是 BSON 文件,恢复的时候是整个库或者整个集合覆盖。这意味着你没法做“只同步某几个字段”或者“只同步满足条件的数据”。而 mongo shake 支持在同步时做字段过滤和条件过滤,你可以只同步需要的列,也可以只同步 status 为 active 的文档。

另一个差异是增量能力。mongodump 每次都是全量,数据量大的时候导出导入都很慢。mongo shake 如果配置了增量字段,第二次之后只同步变化的部分,速度会快很多。我实测过一个 200 万文档的集合,全量同步大概要十几分钟,增量同步每次只要几秒钟。

还有一个容易被忽略的点是目标端的写入策略。mongorestore 默认是插入,遇到冲突会报错。mongo shake 可以配置成 upsert 模式,也就是存在就更新、不存在就插入。这在两边数据需要保持最终一致的场景下非常实用。

2.3 什么情况下不该用它

虽然 mongo shake 在单实例同步场景下很好用,但也不是万能的。有几种情况我建议你换别的方案。

如果两边数据量特别大,比如单个集合上亿文档,那 mongo shake 的全量扫描会比较吃力,因为它是一条条读一条条写,没有批量并行优化。这种场景更适合用 mongodump 做物理备份再恢复,或者上副本集用官方同步机制。

如果源端数据变化非常频繁,比如每秒都有几千条写入,那基于增量字段的同步会有延迟,而且可能漏数据。因为它是定时拉取,不是实时监听。这种场景需要的是 Change Streams 或者 oplog tailing。

如果两边 MongoDB 版本差异很大,比如一个 3.6 一个 6.0,那 mongo shake 的驱动兼容性可能会出问题。我建议两边版本尽量接近,至少大版本不要差太多。

3. 环境准备与基础配置实操

3.1 两边 MongoDB 的前置检查

在开始配置 mongo shake 之前,有几项检查必须做,不然跑到一半报错会很麻烦。

第一项是网络连通性。在跑同步的机器上分别 telnet 一下两边的端口,确认能通。如果是云上的 MongoDB,还要检查白名单有没有把执行同步的机器 IP 加进去。我遇到过好几次都是因为白名单没加,连接超时排查了半天。

第二项是账号权限。源端账号至少要有对应库的 read 权限,目标端账号要有 readWrite 权限。如果目标端要建集合,还需要 dbAdmin 或者对应的 createCollection 权限。建议单独建一个同步专用账号,不要用 admin 账号,方便后续审计。

第三项是版本确认。在两台机器上分别执行db.version(),记下版本号。mongo shake 对 MongoDB 3.6 到 6.0 的支持比较好,再高的版本需要确认驱动是否兼容。

第四项是磁盘空间。目标端的磁盘剩余空间至少要能放下源端要同步的数据量,如果是全量覆盖模式,还要预留临时空间。我一般会留出源数据量 1.5 倍的空间,防止同步过程中出现意外。

3.2 mongo shake 的安装与初始化

mongo shake 的安装方式取决于你拿到的发行包形式。如果是二进制包,直接解压到某个目录,把 bin 路径加到 PATH 里就行。如果是源码,需要先确认本地有对应的编译环境。

安装完成后,第一步是初始化配置目录。mongo shake 通常会有一个默认的配置路径,比如~/.mongo-shake或者当前目录下的conf文件夹。你可以执行初始化命令让它生成一份默认配置模板,然后基于模板修改。

初始化完成后,重点检查两个文件:一个是主配置文件,通常叫mongo-shake.conf或者类似名字;另一个是同步任务定义文件,可能叫sync.task或者collection.map。主配置文件里放的是源端和目标端的连接信息、日志级别、并发数这些全局参数。任务定义文件里放的是具体要同步哪些集合、字段怎么映射、增量字段是哪个。

我建议第一次跑的时候先把日志级别调到 debug,这样能看到详细的读取和写入记录,方便排查问题。等跑通之后再调回 info,减少日志量。

3.3 连接串与认证配置的坑

连接串的写法看起来简单,但实际配置时有几个细节容易出错。

源端连接串的格式一般是mongodb://user:password@host:port/dbname?authSource=admin。这里authSource很关键,如果账号是在 admin 库建的,但你要同步的是业务库,那 authSource 必须写 admin,否则认证会失败。我见过有人把 authSource 写成业务库名字,结果一直报认证错误。

如果密码里有特殊字符,比如@、#、%,一定要做 URL 编码。比如@要写成%40,#要写成%23。这个坑我踩过,密码里有个@,连接串解析的时候把@后面的内容当成了主机名,报错信息完全看不出是密码问题。

目标端连接串同理。如果目标端不需要认证,可以省略用户名密码部分,但建议还是加上,方便后续权限管理。

还有一个细节是连接超时时间。默认的超时可能比较短,如果两边网络延迟高,建议把connectTimeoutMS和socketTimeoutMS调大一些,比如 30000 毫秒。不然同步过程中偶尔会断连。

4. 同步任务的配置与运行

4.1 定义源集合到目标集合的映射

mongo shake 的核心配置就是集合映射。你需要告诉它:源端的哪个库的哪个集合,同步到目标端的哪个库的哪个集合。

配置的写法通常是一个列表,每一项包含 source 和 target 两部分。source 里写库名和集合名,target 里也写库名和集合名。如果两边库名集合名一样,可以简写。如果不一样,就分别写清楚。

这里有个实用技巧:如果源端有多个集合要同步到目标端的同一个库,可以只写一次库级别的映射,然后在下面列出集合级别的对应关系。这样配置更清晰,也方便批量管理。

另外,mongo shake 支持通配符匹配。比如源端有order_202301、order_202302这样的按月分表,你可以配置成order_*同步到目标端的order_all集合。不过通配符同步的时候要注意,目标端如果是同一个集合,可能会有主键冲突,需要配合 upsert 模式使用。

4.2 增量字段的选择与配置

增量同步是 mongo shake 比较实用的功能,但前提是你要选对增量字段。

理想的增量字段应该满足几个条件:单调递增、不重复、有索引。最常见的就是_id或者创建时间字段。如果_id是 ObjectId 类型,它本身是包含时间信息的,而且天然递增,非常适合做增量字段。如果是自增整数 ID,也可以。

配置的时候需要指定字段名和类型。比如incr_field: create_time,类型可能是 date 或者 int。mongo shake 会根据这个字段的值来判断哪些是新数据。每次同步完,它会记录当前的最大值,下次从这个值之后开始读。

这里有个注意事项:如果增量字段的值不是严格递增的,比如有并发写入导致时间戳相同,那可能会漏掉一部分数据。解决办法是配合一个次要排序字段,比如create_time + _id组合。mongo shake 是否支持组合增量字段取决于版本,如果不支持,那就只能接受少量漏数据,或者改用全量同步。

还有一个坑是时区问题。如果增量字段是日期类型,源端存的是 UTC 时间,但你在配置里写的是本地时间,那同步范围就会错。建议统一用 UTC 时间,或者在配置里明确指定时区。

4.3 全量与增量模式的切换逻辑

mongo shake 通常支持三种同步模式:仅全量、仅增量、全量加增量。

仅全量模式就是每次跑都把源端数据全部读一遍写到目标端。适合数据量小、变化不频繁的场景。配置简单,不容易出错。

仅增量模式是只同步增量字段大于上次记录值的数据。适合数据量大、只关心新增数据的场景。第一次跑之前需要手动设置一个起始值,或者先跑一次全量把基线建好。

全量加增量模式是先做一次全量,然后自动切换到增量。这是最常用的模式,既保证了初始数据完整,又能持续同步新数据。配置的时候需要指定全量完成后是否自动进入增量,以及增量的检查间隔。

我一般建议第一次跑用全量加增量,跑通之后如果数据量不大,可以改成每次全量,省得维护增量位点。如果数据量大,就保持全量加增量,但要注意增量位点的持久化,防止重启后丢失。

4.4 启动同步任务与观察日志

配置完成后,启动同步任务的命令通常是mongo-shake start -conf /path/to/config或者类似形式。启动后不要马上走开,先观察前几分钟的日志。

日志里重点看几个信息:连接是否成功、全量扫描开始了没有、扫描速度大概多少、有没有报错。如果看到connection refused或者authentication failed,那就是连接或认证问题,回去检查连接串和账号权限。如果看到duplicate key error,那就是目标端有冲突数据,需要调整写入模式或者先清理目标端。

全量扫描阶段日志会比较多,可以看到已扫描文档数和已写入文档数。如果两个数字差距很大,说明写入有失败,需要看具体错误。如果扫描速度很慢,可能是源端没有索引,或者网络带宽不够。

全量完成后,如果配置了增量,日志会显示进入增量阶段,并打印当前的增量位点。这时候你可以往源端插一条测试数据,看看目标端能不能同步过去。如果能,说明整个链路是通的。

5. 常见问题与排查技巧实录

5.1 连接类问题速查

连接类问题是最常见的,表现就是启动就报错,或者跑着跑着断连。下面这个表是我整理的一些典型现象和排查方向。

现象可能原因排查方法
启动报 connection refused端口不通或服务没起telnet 测试端口,检查 MongoDB 进程
启动报 authentication failed账号密码错或 authSource 不对用 mongo shell 手动连一次验证
跑一段时间后断连超时设置太短或网络抖动调大 timeout,检查网络质量
目标端写入报 not authorized目标账号权限不足确认账号有 readWrite 权限
连接串解析错误密码含特殊字符未编码对密码做 URL 编码

排查连接问题的第一步永远是手动用 mongo shell 连一次。如果 shell 都连不上,那 mongo shake 肯定也连不上。shell 能连上但 shake 连不上,那就是配置格式问题,仔细检查连接串的每个部分。

5.2 数据不一致的排查思路

数据不一致的表现是同步完成后,两边文档数对不上,或者某些文档内容不一样。这个问题排查起来比较费时间,我一般按下面的顺序来。

先确认是不是全量阶段就漏了。可以在源端执行db.collection.count({}),在目标端也执行一次,看数字差多少。如果差得不多,可能是同步过程中源端有新写入,这部分属于正常增量,等增量阶段跑完再对比。

如果差得很多,那可能是全量扫描的时候有条件过滤,或者目标端写入时有失败。检查配置里有没有 filter 条件,检查日志里有没有 write error。

如果文档数一样但内容不一样,那可能是字段映射有问题,或者更新的时候覆盖了不该覆盖的字段。检查字段映射配置,确认 upsert 模式下的更新策略是$set还是$replace。$set只更新指定字段,$replace会替换整个文档。根据业务需求选择。

还有一个隐蔽的问题是数据类型不一致。比如源端的数字是 int32,目标端存成了 int64,虽然值一样,但类型不同。这种问题在业务查询时可能出问题。排查的时候可以用typeof检查关键字段的类型。

5.3 性能调优的几个关键参数

如果同步速度慢,可以从几个方面调优。

并发数是第一个要看的。mongo shake 通常支持配置并发线程数,默认可能比较保守。如果源端和目标端性能都够,可以适当调大,比如从 4 调到 8 或 16。但不要调太大,否则两边压力都会很高。

批量大小是第二个。每次读取和写入的文档数可以配置,默认可能是 100 或 500。调大到 1000 或 2000 可以减少网络往返次数,提高吞吐。但太大会占用更多内存,需要根据机器配置权衡。

索引是第三个。源端的增量字段一定要有索引,否则每次增量查询都是全表扫描,速度会非常慢。目标端的唯一键也要有索引,否则 upsert 的时候会慢。

网络是第四个。如果两边跨机房,网络延迟高,那同步速度天然受限。可以考虑在目标端同机房部署一个中间节点,先把数据同步到中间节点,再转到目标端。不过这样架构就复杂了,一般场景没必要。

5.4 增量位点丢失怎么办

增量位点丢失是个比较麻烦的问题。表现是重启 mongo shake 后,它从很早的位置开始同步,导致大量重复数据。

位点通常存在某个地方,可能是本地文件,也可能是目标端的一个特殊集合。如果存在本地文件,那容器重启或者机器迁移的时候容易丢。如果存在目标端集合,相对安全一些,但也要注意备份。

如果位点丢了,最简单的办法是重新做一次全量,把基线重建。如果数据量太大不想全量,可以手动去目标端查当前最大的增量字段值,然后把这个值写到配置里作为起始位点。但这样可能会漏掉一部分数据,需要评估影响。

预防位点丢失的办法是定期备份位点文件,或者把位点存到目标端的一个独立集合里,并且给这个集合做定期备份。

6. 实际项目中的经验总结

6.1 我踩过的三个坑

第一个坑是时区。有一次增量字段是日期类型,源端存的是 UTC,我在配置里没注意,用了本地时间做起始值,结果同步出来的数据少了 8 小时。后来统一改成 UTC 才正常。这个问题的隐蔽性在于,数据看起来是同步过去了,但时间字段的值不对,业务查询的时候才会发现。

第二个坑是目标端集合已存在但结构不一样。mongo shake 默认可能不会自动建集合,如果目标端已经有同名集合但字段类型不同,写入的时候会报类型错误。解决办法是同步前先确认目标端集合结构,或者配置成自动重建集合。但自动重建会清空原有数据,要谨慎使用。

第三个坑是增量字段有重复值。有一次源端的 create_time 精度只到秒,同一秒内有多条数据,增量同步的时候只同步了其中一部分。后来改成用_id做增量字段才解决。所以选增量字段的时候一定要确认唯一性。

6.2 什么场景下我会推荐用 mongo shake

根据我的经验,下面这几种场景用 mongo shake 比较合适。

开发环境定期从测试环境拉数据。配置一个定时任务,每天凌晨跑一次全量同步,把测试环境的数据搬到开发环境。开发人员第二天来就能用最新数据调试。

两个独立系统之间的基础数据同步。比如两个业务系统各自有用户表,但用户基础信息需要保持一致。可以配置增量同步,一边更新另一边跟着更新。

数据迁移过程中的过渡方案。比如要从旧机器迁移到新机器,但业务不能停。可以先用 mongo shake 做一次全量,然后开增量同步,等增量追上之后再切流量。这样停机时间可以缩短到几分钟。

临时数据分析场景。需要把生产环境某个集合复制到分析机器上跑查询,不想影响生产。用 mongo shake 同步一份过去,分析完直接删掉。

6.3 后续可以扩展的方向

如果 mongo shake 的基本功能满足不了需求,可以考虑几个扩展方向。

一个是加监控告警。同步任务跑失败的时候要能及时知道,可以监控日志里的 error 关键字,或者监控目标端的文档数变化。如果长时间没有新增,就发告警。

另一个是加数据校验。同步完成后自动对比两边的文档数和关键字段的校验和,确保数据一致。这个可以用脚本实现,跑在同步任务之后。

还有一个是做成定时任务。如果同步频率固定,可以用 cron 或者类似的调度工具定时拉起 mongo shake,跑完自动退出。这样不用一直挂着进程,资源占用更少。

最后再分享一个小技巧:如果两边数据量不大,但集合特别多,可以写一个脚本自动生成 mongo shake 的配置文件。从源端读出所有集合名,然后按规则生成映射关系,省得手动一个个写。这个脚本我写过一版,大概几十行 Python,有需要的可以自己实现一下。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询