2. 增量同步原理
大家好,我是老赵。今天咱们聊聊增量同步的核心原理。说实话,很多刚入行的朋友一上来就问我:“老赵,为什么不能每次都全量同步?多省事啊!”嗯,这个问题我当年也问过,直到我在一个日活千万的电商项目里,亲眼看着全量同步把生产库拖垮了……从那以后,我就再也不敢小看增量同步了。
全量同步 vs 增量同步:两个极端
先说说全量同步。说白了,就是把源端的数据整个复制一份到目标端。听起来简单粗暴,对吧?但代价也很大。
全量同步的典型场景: 初次搭建灾备环境、数据恢复后的重新对齐、小数据量的定时备份。
我个人的习惯是:能用增量,绝不用全量。除非数据量小于 10GB,或者这是第一次做同步。为什么?你想想看,一个 2TB 的数据库,全量同步一次可能要跑 8 小时,期间源库的 IO 被打满,业务直接受影响。我在项目中遇到过,全量同步导致主库响应时间从 5ms 飙升到 2s,差点被运维同学拉去“喝茶”。
| 对比维度 | 全量同步 | 增量同步 |
|---|---|---|
| 数据量 | 全部数据 | 仅变更数据 |
| 同步周期 | 长(小时级) | 短(秒级/分钟级) |
| 源库压力 | 极高(全表扫描) | 低(仅读取变更) |
| 断点续传 | 需重新开始 | 天然支持 |
| 适用场景 | 初始化、数据修复 | 持续同步、容灾 |
增量同步的核心思路就一句话:只传输变化的数据。但问题来了——怎么知道哪些数据变了?这就引出了两种主流方案:基于日志的增量捕获(CDC)和基于时间戳的增量同步。
基于日志的增量捕获(CDC)
CDC,全称 Change Data Capture。我个人认为,这是目前最靠谱的增量同步方案。它的原理很简单:数据库的每一次写入、更新、删除,都会记录在事务日志里(比如 MySQL 的 binlog、PostgreSQL 的 WAL)。我们只需要解析这些日志,就能拿到精确的变更数据。
我的经验: 在金融项目中,我强制要求使用 CDC。因为基于日志的捕获是“物理级”的,不会漏掉任何一条变更,哪怕是一条回滚的事务,日志里也有记录。这对于对账和审计来说,太重要了。
CDC 的工作流程大致如下:
- 日志读取器:连接到源库,实时读取事务日志。
- 日志解析器:将二进制日志解析成结构化数据(INSERT、UPDATE、DELETE)。
- 变更分发器:将解析后的变更事件发送到目标端(消息队列或直接写入)。
- 目标应用器:在目标库上回放这些变更。
这里有个坑,我曾经踩过:日志的消费位置。如果 CDC 进程挂了,重启后从哪里开始读?答案是:记录日志的偏移量(offset)或 LSN(Log Sequence Number)。只要把偏移量持久化到外部存储(比如 ZooKeeper 或数据库),就能实现断点续传。
警告: 不要依赖 CDC 工具的默认内存缓存偏移量。我曾经遇到过,CDC 进程 OOM 重启后,偏移量丢失,导致重复消费了 30 分钟的日志,目标库多出了几百万条重复数据。修复花了整整一个通宵。
下面这张图,是我常用的 CDC 架构,你可以直观地看到数据流向:
基于时间戳的增量同步
另一种方案,就是基于时间戳。原理更简单:在源表上加一个 update_time 字段,每次同步时,查询“上次同步时间之后修改过的所有记录”。
听起来很美好,对吧?但我在项目中遇到过不少坑:
- 时间精度问题:如果两条记录在同一毫秒内更新,可能会漏掉一条。
- 删除操作无法捕获:记录被删了,时间戳字段也没了,你根本不知道它曾经存在过。
- 大事务延迟:一个事务更新了 10 万行,时间戳都是同一个值,但同步程序可能只读到一部分。
避坑指南: 我曾经在一个 CRM 系统里用时间戳做增量同步,结果因为业务方批量修改数据时没更新 update_time,导致漏掉了 2 万条客户记录。从那以后,我定了一条铁律:时间戳方案必须配合软删除(逻辑删除)和全量对账机制。
下面是一个简单的时间戳增量同步伪代码:
-- 源表结构
CREATE TABLE orders (
id BIGINT PRIMARY KEY,
order_no VARCHAR(64),
status INT,
update_time DATETIME(3) INDEX -- 毫秒精度索引
);
-- 增量查询(假设上次同步时间点为 '2024-01-15 10:00:00.000')
SELECT * FROM orders
WHERE update_time > '2024-01-15 10:00:00.000'
AND update_time <= NOW()
ORDER BY update_time ASC
LIMIT 1000; -- 分批处理,避免大事务
注意看,我加了 LIMIT 和 ORDER BY。为什么?因为如果不分批,一次查询几百万条,内存直接爆掉。而且必须按时间排序,否则可能出现“后更新的记录先被消费”的情况,导致目标库数据不一致。
两种方案的对比与选择
说了这么多,到底该选哪个?我个人的决策逻辑是这样的:
| 判断条件 | 推荐方案 | 理由 |
|---|---|---|
| 源库支持事务日志(MySQL/PostgreSQL/Oracle) | CDC | 无侵入、不丢数据、支持删除 |
| 源库是 NoSQL 或老系统,没有日志 | 时间戳 | 唯一可行的方案 |
| 数据一致性要求极高(金融、对账) | CDC | 日志是物理级,不会漏 |
| 开发资源有限,快速上线 | 时间戳 | 实现简单,但要做好补偿机制 |
| 需要同步删除操作 | CDC | 时间戳无法捕获物理删除 |
我的建议: 如果条件允许,优先上 CDC。虽然初期搭建成本高一些(需要部署解析组件、消息队列),但长期来看,运维成本远低于时间戳方案。你想想看,时间戳方案需要定期对账、处理边界情况、补偿漏掉的删除操作……这些隐性成本加起来,足够你买好几台服务器了。