必须用控制表持久化同步状态,否则会漏数据或重复插入;仅靠SELECT MAX(update_time)无法获取真实同步起点;MERGE或ON DUPLICATE KEY UPDATE不处理删除,需单独实现带时间过滤的DELETE;空值、精度、索引、时区均需严格处理。

不能靠存储过程本身“记住”上次同步点,必须用控制表持久化状态,否则每次调用都是无状态重试,漏数据或重复插入是必然结果。
为什么不能只靠 SELECT MAX(update_time) 临时算同步起点
存储过程每次执行都在新会话中,局部变量一退出就销毁。你无法在 v_last_time 里存住上一次的值。
- 常见错误:过程开头写
SELECT MAX(update_time) INTO v_last_time FROM target_table,以为这就是“上次同步时间”——其实它只是目标表当前最新时间,和源表变更完全脱节 - 更危险的是,如果目标表刚被清空但事务未提交,
MAX()返回NULL,后续WHERE update_time > NULL整个条件恒假,一条数据都拉不到 - 正确做法是查控制表:
SELECT last_sync_time INTO v_last_time FROM sync_control WHERE table_name = 'orders'
MERGE 或 INSERT ... ON DUPLICATE KEY UPDATE 只解决增/改,删必须单独写
MERGE(Oracle/SQL Server)或 INSERT ... ON DUPLICATE KEY UPDATE(MySQL)本质是“源驱动”,只管把源里的行映射到目标,对源端已删除的记录毫无感知。
- 不补删除逻辑,目标表就会越积越多“孤儿数据”
- 删除语句必须加时间过滤,例如:
DELETE FROM target WHERE id NOT IN (SELECT id FROM source WHERE update_time > v_last_time) AND update_time ,否则刚同步进来的新记录可能被误删 - PostgreSQL 没有标准
MERGE,只能用INSERT ... ON CONFLICT DO UPDATE+ 显式DELETE两步走
空值、时区、索引这三件事不处理,同步就不可靠
看似简单的 WHERE update_time > ?,实际运行中崩得悄无声息。
-
update_time IS NULL的行永远进不了增量流程,必须显式排除:AND update_time IS NOT NULL - MySQL 的
DATETIME默认秒级精度,但业务可能毫秒更新;字段定义要是DATETIME(3),同步程序也得匹配精度,否则同毫秒内的多条记录只有一条能被拉到 - 没给
update_time建索引?全表扫描会让同步变慢甚至拖垮源库——哪怕只有 10 万行 - 时区不统一是隐形杀手:应用写入用
Asia/Shanghai,数据库设UTC,同步脚本又按本地时间解析,差 8 小时就直接漏掉一整天数据
真正难的不是拼出能跑通的 SQL,而是让这套逻辑在连续运行半年后,还能准确回答“这次同步到底拉了哪几条记录”。日志要记、位点要落盘、删逻辑不能省、空值和精度不能赌运气——这些地方少做一步,生产环境就多一个深夜告警。

















