CDC中UPDATE操作默认生成两行($operation=3和4),需用row_filter_option或net_changes函数控制;源表必须有主键或唯一索引,LSN范围须严格递进,且需解析$update_mask避免误写NULL。

UPDATE操作在CDC中实际生成两行记录
CDC对UPDATE的捕获不是“一行新值”,而是把更新前后的状态都保留下来:旧值行的__$operation为3,新值行为4。这意味着直接查cdc.fn_cdc_get_all_changes_*默认返回两行,不加筛选会重复处理或写入失败。
常见错误现象是同步程序把同一笔UPDATE当成两次独立变更,导致目标库主键冲突或数据错乱。关键在于区分“变更事件”和“变更后快照”——CDC提供的是前者,你得自己选后者。
- 用
row_filter_option = 'all update old'可强制只取新值(__$operation = 4),但注意这会丢弃旧值,无法做字段级比对 - 若需判断哪些字段被改过,必须同时读
__$operation = 3和=4两行,再用JOIN或LAG()比对列值 -
supports_net_changes = 1启用时,系统会额外生成cdc.fn_cdc_get_net_changes_*函数,它自动合并UPDATE为单行(仅含最终值),但要求表有主键或唯一索引
必须确保源表有主键或唯一约束
没有主键的表启用CDC后,cdc.fn_cdc_get_net_changes_*函数会报错:Msg 313, Level 16, State 3, Line 1: An insufficient number of arguments were supplied for the procedure or function cdc.fn_cdc_get_net_changes_...。这不是权限问题,是CDC底层依赖唯一标识定位“同一行的多次变更”。
即使你只用all模式查所有变更,没主键也会让同步逻辑脆弱:UPDATE前后两行无法可靠关联,id字段相同但其他列可能因并发修改而无法归因。
- 添加主键:如
ALTER TABLE dbo.t_cdc_ta ADD CONSTRAINT PK_t_cdc_ta PRIMARY KEY (id); - 若不能改表结构,可用唯一索引替代:
CREATE UNIQUE INDEX UX_t_cdc_ta_id ON dbo.t_cdc_ta(id); - 检查是否就绪:
SELECT OBJECTPROPERTY(OBJECT_ID('dbo.t_cdc_ta'), 'TableHasPrimaryKey')返回1才安全
LSN范围控制是避免漏同步或重复同步的核心
CDC不提供“上次同步时间”这种直观标记,全靠日志序列号(LSN)推进。每次调用cdc.fn_cdc_get_all_changes_*必须传from_lsn和to_lsn,且to_lsn必须来自sys.fn_cdc_get_max_lsn(),否则可能跳过未提交的变更。
典型陷阱是把to_lsn设成固定值或用GETDATE()模拟时间窗口——LSN不是时间戳,它按事务提交顺序严格递增,跨服务器/跨库不可比。
- 首次同步:用
sys.fn_cdc_get_min_lsn('capture_instance_name')作起点 - 后续同步:上一轮返回的
__$start_lsn最大值 + 1,作为下一轮from_lsn - 务必在查询后立即保存最新
to_lsn(用sys.fn_cdc_get_max_lsn()),避免代理作业清理掉未读LSN
UPDATE同步时别忽略__$update_mask位图
当只捕获部分列(通过@captured_column_list指定)时,__$update_mask告诉你本次UPDATE实际改了哪些列。它是varbinary(128),每一位对应一个捕获列,值为1表示该列被更新过。
例如表有name、addr、ttime三列被捕获,某次UPDATE只改了name,则__$update_mask二进制为001(低位在右)。不检查这个,容易把NULL覆盖原值——比如addr没变却被同步为NULL。
- 解析方法:
CASE WHEN SUBSTRING(__$update_mask, 1, 1) & 1 = 1 THEN name END逐列判断 - 更稳妥的做法:同步SQL中用
COALESCE(new.name, old.name),前提是能关联到旧值行 - 如果业务允许,启用CDC时直接省略
@captured_column_list,捕获全量列,避开位图解析复杂度
LSN推进逻辑和__$update_mask解析是UPDATE同步里最容易被跳过的两步,跳过它们,同步看起来跑通了,但数据一致性会在某次并发更新后突然崩塌。

















