分布式数据库在全量同步和增量同步之间切换,核心就是解决"从零开始追数据"和"从断点继续追数据"两种场景的平滑过渡问题。实际操作中,最常见的做法是:先用全量同步把目标库的数据底座搭好,再无缝切换到增量同步去追赶全量期间产生的变更,最终让目标库和源库完全一致。这套策略看起来简单,但真正落地时会遇到数据一致性校验、切换时间窗口、断点续传精度等一堆坑,下面我把每一步拆开讲透。
先说结论:全量同步转增量同步的关键在于三个动作——记录全量开始时的位点(binlog位点或LSN)、在全量完成后对比目标库与源库的数据差异、然后从正确的位点启动增量同步。如果这三步有任何一步做歪了,轻则数据重复,重则数据丢失或不一致。
一、全量同步和增量同步分别是什么,为什么要切换全量同步,就是把源数据库里所有的表、所有的数据一次性搬到目标库。适合首次初始化、目标库为空、或者数据差异太大需要重新对齐的场景。缺点是数据量大的时候耗时长,对源库压力大,期间产生的新数据如果不处理就会丢。
增量同步,就是只同步全量之后新产生的数据变更,通常基于binlog、WAL日志或者CDC(变更数据捕获)机制来实现。它的优势是轻量、实时、对源库影响小,但前提是目标库已经有了一个和源库基本一致的数据基础。
所以切换的本质就是:用全量打底,用增量追赶,最终让两个库完全对齐。这在数据库迁移、多活架构搭建、读写分离从库初始化等场景中都是标配操作。
二、全量同步阶段必须做好的准备工作很多人觉得全量同步就是导个数据,其实不是。在启动全量之前,你必须做以下几件事:
第一,记录同步起点位点。比如MySQL要记录binlog的file和position,PostgreSQL要记录LSN。这个位点是后续增量同步的起始锚点,记错了后面全白干。
第二,评估全量同步对源库的影响。大表全量导出时如果不加锁或者不控制并发,可能把源库打挂。建议用低优先级的备份工具,或者在从库上做全量导出,别直接怼主库。
第三,确定全量同步期间源库的写入策略。如果业务允许,可以在全量期间短暂降低写入频率;如果不允许,就必须接受全量期间会产生一批"全量期间的增量数据",这批数据要在后续增量阶段补上。
-- MySQL记录binlog位点示例 SHOW MASTER STATUS; -- 结果示例:File: mysql-bin.000003, Position: 154 -- 把这个值记下来,作为增量同步的起点三、全量完成后的数据校验与差异处理
全量同步跑完不代表万事大吉。你必须做数据校验,确认目标库的数据和源库在全量开始那一刻是一致的。常用的校验方法有三种:
第一种,行数对比。对每张表做count对比,快速但不精确,只能发现明显差异。
第二种,checksum对比。用pt-table-checksum或者自定义的MD5聚合校验,按主键分组做哈希比对,能精确定位到哪些行有差异。
第三种,抽样对比。对关键业务表做全字段抽样比对,适合数据量特别大、全量校验成本太高的情况。
如果校验发现有差异,说明全量期间源库有写入,导致目标库数据滞后。这时候你需要计算出全量开始位点到全量结束时刻之间产生了多少增量数据,然后在增量同步阶段把这段"缺口"补上。具体做法是:增量同步的起始位点要设置为全量开始时记录的位点,而不是全量结束时的位点。这样增量同步会自动覆盖全量期间的所有变更。
四、增量同步启动与切换的核心步骤切换到增量同步的操作流程,我总结为五步:
第一步,停止全量同步任务,确保没有新的全量数据写入目标库。
第二步,确认增量同步的起始位点。这个位点必须是全量开始时记录的那个,不是当前的。如果你用的是Canal、Debezium这类CDC工具,直接配置从记录的binlog位点开始消费即可。
第三步,启动增量同步任务,并监控同步延迟。刚启动时可能会有一批积压的binlog事件需要处理,延迟会比较高,这是正常的。等积压消化完,延迟应该降到秒级甚至毫秒级。
第四步,持续做数据一致性校验。在增量同步运行期间,定期对比源库和目标库的关键指标,确保没有数据丢失或重复。
第五步,当同步延迟稳定在可接受范围(比如1秒以内),且多次校验一致后,宣布切换完成,目标库可以正式上线提供服务。
-- Debezium配置从指定binlog位点启动的示例配置
{
"name": "inventory-connector",
"config": {
"connector.class": "io.debezium.connector.mysql.MySqlConnector",
"database.hostname": "source-db",
"database.port": "3306",
"database.user": "debezium",
"database.password": "dbz",
"database.server.id": "184054",
"database.server.name": "dbserver1",
"database.history.kafka.bootstrap.servers": "kafka:9092",
"database.history.kafka.topic": "schema-changes.inventory",
"snapshot.mode": "when_needed",
"database.include.list": "inventory",
"table.include.list": "inventory.products,inventory.orders"
}
}
五、切换过程中最容易踩的五个坑
坑一:位点记录错误。全量开始时没记录位点,或者记录的是全量结束时的位点,导致增量同步要么重复同步全量数据,要么漏掉全量期间的变更。解决办法是在全量任务启动脚本里强制写入位点到持久化存储,别靠人工记忆。
坑二:全量期间源库有DDL变更。如果全量同步过程中源库执行了ALTER TABLE、DROP TABLE等操作,目标库可能同步失败或者结构不一致。建议全量期间禁止DDL,或者使用支持DDL同步的工具。
坑三:增量同步重复消费。如果增量任务重启时没有正确记录消费位点,可能从更早的位置重新消费,导致目标库出现重复数据。解决办法是使用支持exactly-once语义的消息队列,或者在目标库做幂等写入(比如用主键去重)。
坑四:大事务导致增量延迟。源库如果有一个超大事务(比如一次更新百万行),binlog里这个事务会作为一个整体事件发送,目标库执行这个事务也需要很长时间,导致延迟飙升。应对方法是在源库层面拆分大事务,或者在目标库配置并行应用线程。
坑五:切换窗口期业务无保护。从全量切到增量的那几分钟,如果业务还在往源库写,而目标库还没开始接收增量,这段时间的数据就丢了。所以切换动作要快,最好在业务低峰期操作,或者用双写机制做过渡保护。
六、不同数据库的切换策略差异MySQL生态:最成熟,binlog机制完善,工具链丰富。Canal、Debezium、Maxwell都支持从指定位点启动增量同步。全量一般用mysqldump、mydumper或者Xtrabackup。
PostgreSQL:用逻辑复制(logical replication)做增量,全量用pg_dump或者pg_basebackup。注意PostgreSQL的LSN位点和MySQL的binlog位点不一样,切换时要用对应的工具获取。
TiDB、OceanBase等分布式数据库:自带数据同步工具,比如TiDB的TiCDC、OceanBase的OMS。这些工具通常把全量和增量封装在一个任务里,切换逻辑内置,但你仍然需要理解底层原理,否则出问题不知道怎么排查。
MongoDB:用oplog做增量,全量用mongodump或者mongorestore。切换时要注意oplog的时间戳位点记录,以及分片集群的特殊处理。
七、生产环境的最佳实践建议第一,永远不要在没有校验的情况下宣布切换完成。至少跑三轮以上的全量校验,每轮间隔不少于10分钟,确认无差异再上线。
第二,把切换过程自动化。写成脚本或者编排到CI/CD流程里,减少人为失误。位点记录、校验、增量启动这些步骤都应该是一键触发的。
第三,保留回滚方案。如果切换后发现数据不对,要能快速切回源库或者重新做一次全量同步。所以全量同步的数据备份不要急着删,至少保留到增量同步稳定运行24小时之后。
第四,监控要到位。同步延迟、错误日志、数据行数波动这些指标都要上监控大盘,设置告警阈值。延迟超过5秒就该排查了。
第五,文档化。每次切换的位点、时间、校验结果、异常处理都要记录下来,形成知识库。下次再做类似操作,直接复用经验,不用从头摸索。
八、总结分布式数据库全量转增量同步,说白了就是"先搬家再补货"的逻辑。搬家(全量)要稳、要准,补货(增量)要快、要全。核心抓手是位点管理和数据校验,这两件事做扎实了,切换就不会出大问题。不同数据库有不同的工具和细节,但底层思路是通用的。把这套策略吃透,不管是做迁移、做多活还是做容灾,你都能心里有底。
