CDC才是生产环境真正可靠的增量同步路径。时间戳方案存在删数据、批量更新漏字段、时钟漂移、空值、精度错配等硬伤;CDC通过捕获事务日志天然支持全事件,不依赖业务字段维护。

时间戳方案够用但有硬伤,CDC才是生产环境真正可靠的增量同步路径。 时间戳能解决 80% 的简单场景,但删数据、批量更新漏字段、时钟漂移、空值、精度错配等问题一来就崩;而 CDC(如 MySQL binlog、SQL Server CDC、PostgreSQL logical replication)直接捕获事务日志,天然支持 INSERT/UPDATE/DELETE 全事件,不依赖业务代码维护字段,也不怕应用层绕过 ORM 更新。
WHERE updated_at > ? 查询为什么总漏数据或重复
这不是 SQL 写错了,而是底层假设被现实击穿了:
- 字段没索引 → 每次全表扫描,
updated_at > ?变成性能黑洞 - 用了
>=而非>→ 高并发下最后一条可能被重复拉两次(尤其当多线程同时取最大时间戳再查询时) - 数据库时区和应用不一致 → 比如 MySQL 存的是系统时区
2024-06-01 10:00:00,Java 程序却按 UTC 解析,实际比对变成2024-06-01 02:00:00,跳过整整 8 小时变更 -
updated_at为空或被批量脚本忽略 → 某些 ETL 工具或旧版 ORM 批量 UPDATE 不触发自动更新,导致这批记录永远进不了增量流 - 精度不匹配 → MySQL 5.6
DATETIME只到秒,但程序用LocalDateTime.now()带毫秒,存进去截断后查> '2024-06-01 10:00:00.123'就漏掉同一秒内所有变更
SQL Server 启用 CDC 必须做的三件事
CDC 不是开个开关就能用,必须显式完成初始化链路,否则 cdc.fn_cdc_get_all_changes_* 返回空:
- 以
sysadmin角色执行EXEC sys.sp_cdc_enable_db—— 数据库级开关,缺它整个库的 CDC 表都建不起来 - 对目标表启用:
EXEC sys.sp_cdc_enable_table @source_schema = 'dbo', @source_name = 'orders', @role_name = NULL, @supports_net_changes = 1—— 注意@role_name设为NULL才允许非 sa 用户读 CDC 表,否则权限报错 - 确认 SQL Server Agent 正在运行 —— CDC 的捕获作业(
cdc.[db_name]_capture)由 Agent 触发,停了就不再写变更到cdc.dbo_orders_CT表
验证是否生效:查 SELECT * FROM cdc.change_tables,有记录且 is_tracked_by_cdc = 1 才算成功。
MySQL binlog 增量同步前必须检查的配置项
binlog 不是默认就支持行格式变更捕获的,以下三项任一缺失都会让下游收不到 UPDATE/DELETE 的字段级变化:
-
binlog_format = ROW——STATEMENT或MIXED格式下,UPDATE orders SET status='shipped'只记 SQL 文本,下游无法知道哪一行变了;必须ROW才记录前镜像(before image)和后镜像(after image) -
binlog_row_image = FULL—— 默认值,确保 DELETE 和 UPDATE 包含完整行数据;若设为MINIMAL,UPDATE 只传变更列,DELETE 甚至不传主键,同步程序根本无法定位目标行 - 用户权限包含
REPLICATION SLAVE和REPLICATION CLIENT—— 这不是可选,是SHOW MASTER STATUS和消费 binlog 的最低权限,少一个就连接失败
检查命令:SHOW VARIABLES LIKE 'binlog_format';、SHOW VARIABLES LIKE 'binlog_row_image';、SHOW GRANTS FOR 'sync_user'@'%';
游标分页替代 OFFSET 是为了防“位置漂移”
用 LIMIT 1000 OFFSET 10000 分批拉 updated_at 数据,在写入密集场景下必然出错:新插入的记录会把原有第 10001 行“挤下去”,导致下一批从 11001 开始,漏掉被挤入 10001~11000 区间的行。
正确做法是游标分页:
SELECT * FROM orders
WHERE updated_at > '2024-06-01 10:00:00'
AND (updated_at, id) > ('2024-06-01 10:00:00', 12345)
ORDER BY updated_at, id
LIMIT 1000;关键点:
- WHERE 条件里必须同时用时间 + 主键构成复合游标,防止同一秒内多条记录排序不稳定
-
(updated_at, id)上必须建联合索引,否则ORDER BY强制 filesort,大数据量直接卡死 - 每次取结果集最后一行的
(updated_at, id)当作下一批起点,而不是只记时间
这个细节在小数据量测试时完全暴露不出问题,等单表上亿才突然发现每天漏几千条——最容易被忽略,也最难回溯修复。

















