增量同步必须有可追踪的变更字段,如update_time或单调递增id,否则无法界定新/改数据;硬写时间窗口会漏数据,且需处理NULL值、建唯一索引、用ROW_NUMBER()去重,并补DELETE逻辑防孤儿数据。

增量同步必须有可追踪的变更字段
没有 update_time、last_modified 或单调递增的 id,就无法界定“哪些是新/改数据”。硬写 SYSDATE - 1/24 这类时间窗口会漏数据:任务延迟 2 小时,中间的变更就丢了。
常见错误现象:同步后目标表数据比源表少,但查不出原因;或者某天批量更新后,update_time 没被更新,导致后续同步跳过这批记录。
- 字段类型优先选
TIMESTAMP(Oracle/PostgreSQL)或DATETIME(3)(MySQL 5.7+),避免秒级精度丢毫秒变更 - 上线前务必验证业务代码是否真实触发该字段更新——比如 ORM 的
save()是否带update_fields参数绕过了时间戳自动更新 - 空值必须处理:
WHERE update_time > v_last_sync_time AND update_time IS NOT NULL,否则 NULL 行永远不进同步流程
MERGE 是 Oracle/SQL Server 增量同步的核心语法,但不是万能的
MERGE 能原子化处理新增和修改,但它完全不感知“删除”。如果源表删了一条记录,MERGE INTO target USING source ON ... 不会把它从目标表踢掉——这叫“孤儿数据”,生产环境必须补 DELETE 步骤。
容易踩的坑:
-
ON条件字段没建唯一索引 → 可能锁整张表,或匹配逻辑错乱(如用email判重却只在ON里写了id) - 源数据含重复键值 → 直接报错
The MERGE statement attempted to update or delete the same row more than once,得提前用ROW_NUMBER() OVER (PARTITION BY key_col ORDER BY update_time DESC)去重 - Oracle 中
MERGE不支持WHEN NOT MATCHED BY SOURCE,删逻辑只能靠额外DELETE语句,且要加AND update_time 防止误删刚同步进来的新记录
同步状态不能存在变量里,必须落盘到控制表
定时任务(如 DBMS_SCHEDULER 或 SQL Server Agent)每次以新会话运行,局部变量 v_last_sync_time 无法跨次传递。断点续传全靠一张控制表。
示例控制表结构(Oracle):
CREATE TABLE sync_control ( table_name VARCHAR2(30) PRIMARY KEY, last_sync_time TIMESTAMP, last_handle_time TIMESTAMP );
关键点:
- 每次成功同步后,必须用
UPDATE sync_control SET last_sync_time = v_current_max_time WHERE table_name = 'orders',不能只靠SELECT MAX(update_time)临时算 - 失败时,
EXCEPTION块里必须插入日志(如sync_log表),否则失败无声,没人知道停在哪了 - 三步操作(
MERGE+DELETE+ 更新sync_control)必须包在同一个事务里,任一失败则ROLLBACK
MySQL 存储过程做增量同步要防“伪增量”
MySQL 没有原生 MERGE,常用 INSERT ... ON DUPLICATE KEY UPDATE 替代,但它只解决增和改,删仍需单独语句。更隐蔽的问题是:如果源表没显式维护 update_time,而靠 NOW() 插入,那批量导入历史数据时,所有行时间戳一样,下轮同步会全部被跳过。
实操建议:
- 初始化时补全历史时间:
UPDATE source_tab SET update_time = created_time WHERE update_time IS NULL - 存储过程中取位点别用
SELECT MAX(update_time) INTO last_sync_time FROM target—— 目标表可能还没来得及写完,应从源表查:SELECT MAX(update_time) INTO v_current_max FROM source WHERE update_time > v_last_sync_time - 加
FOR UPDATE锁住源表最大时间点,防止并发任务读到未提交的脏时间戳
同步最难的从来不是写对那几行 SQL,而是确保变更字段真正在业务中被更新、位点真正在失败后回退、删除逻辑真正在每个周期执行。这三个“真”字,漏一个,数据就不一致。

















