先说结论:直接用 SQL 触发器实现跨数据库平台的增量数据捕获,这条路走不通。触发器本质上是个数据库内部的同步钩子,它跑在源库的上下文里,既读不到目标库的 binlog 或 WAL,也没办法跨平台解析事务日志,更谈不上保证语义一致性。强行模拟,轻则静默失败,重则数据直接丢失。
为什么触发器无法跨平台捕获增量
触发器只响应本地 DML 事件,它赖以工作的 inserted 和 deleted 表作用域仅限于当前数据库实例,生命周期跟着事务一起销毁。而跨平台面对的是完全不同的日志体系——MySQL 的 binlog、PostgreSQL 的 WAL、SQL Server 的 transaction log,格式互不兼容,触发器根本没能力解析。更棘手的是,缺乏统一的操作类型标识:SQL Server 触发器里没有 TG_OP(这是 PostgreSQL 的特有概念),MySQL 甚至连类似字段都不提供。时间戳精度也是个大问题:GETDATE()(SQL Server)、NOW(6)(MySQL)、CLOCK_TIMESTAMP()(PG)返回值格式不一,根本无法排序定序。再加上类型系统差异导致的隐式转换失败——比如 SQL Server 的 datetime2(7) 写入 MySQL 的 datetime 会直接截断微秒,而且不会有任何报错提示,你连自己丢了数据都不知道。
试图用触发器“模拟”跨平台同步的典型失败点
市场上不乏尝试用触发器强上跨平台的案例,但几乎都踩坑了:
- 在 SQL Server 触发器里拼
OPENQUERY发送到 MySQL 链接服务器:需要装 ODBC 驱动、注册 provider、开启Ad Hoc Distributed Queries,操作链条又长又脆弱。而 MySQL ODBC 对INSERT ... ON DUPLICATE KEY UPDATE这类语法支持不全,经常静默失败,连个错误都不报。 - 用触发器写本地
cdc_log表,再由外部脚本轮询同步:脚本如果没按txid或单调递增序列消费,一旦遇到网络中断,部分变更就会直接跳过,再也补不回来。 - 依赖
last_modified字段做增量标识:这个字段由应用层更新,如果某次 UPDATE 忘记设置它,或者批量导入绕过了触发器,记录就永远丢失了。 - 把
INSERTED转 JSON 后通过 HTTP POST 到中间服务:触发器里调用sp_OACreate默认是禁用的,而且超时设置完全不可控,一次请求卡住就能把整个事务拖死。
真正可行的跨平台增量路径
放弃触发器作为捕获主体,把目光转向日志解析类方案才是正解。具体来说:
- SQL Server 侧启用 CDC(需要 Enterprise 版)或者轻量级的 Change Tracking(虽然不捕获 old 值,但胜在简单);
- MySQL 侧监听 binlog(必须用 ROW 格式),配合 Debezium 或 Maxwell 进行解析;
- 中间层用 Kafka 或 Pulsar 做缓冲,确保至少一次投递;
- 消费端按照 table + pk 做幂等写入,对 MySQL 目标表使用
INSERT ... ON DUPLICATE KEY UPDATE或临时表 merge; - 所有字段映射、类型转换、空值处理逻辑都集中在消费端,不要硬编码在数据库内部。
从工程实践来看,触发器只适合同一实例内的多库同步,或者极简场景下在本地写变更日志。跨平台这件事,从一开始就不该让它来承担。
