触发器不能实现真正的增量同步,仅能通过写变更日志表供外部程序轮询消费来模拟;因其作用域限于单实例、不支持网络调用、无法捕获完整旧值、无位点机制且易致事务失败,生产环境应使用binlog+Canal/Debezium等方案。

触发器本身不支持“增量同步”这个概念——它只响应单条语句的执行,无法感知“自上次以来新增了哪些数据”。所谓“用触发器实现增量同步”,本质是**用触发器打标记或写日志,再由外部程序轮询或消费这些标记/日志来模拟增量**。直接靠触发器把数据推到另一张表或另一库,属于强耦合、易出错的同步方式,不是真正的增量同步方案。
为什么不能直接用 AFTER INSERT 触发器做跨库增量同步
MySQL 的触发器作用域严格限制在当前会话、当前数据库实例内:INSERT INTO other_db.table 在跨库时可能报错(取决于 SQL mode 和权限),而跨服务器根本不可行;INSERT ... SELECT 也不能访问远程表;更关键的是,触发器无法捕获 UPDATE 或 DELETE 中被修改前的完整旧值(OLD.* 只在当前语句上下文有效),也无法保证事务一致性——比如主表插入成功但目标表写入失败,就会丢数据。
- 触发器执行失败会导致源表 DML 失败(除非用
CONTINUE HANDLER,但会掩盖问题) - 高并发下多个触发器同时写同一张日志表,容易产生锁争用或主键冲突
- 没有位点(binlog position)或时间窗口概念,无法判断“哪部分还没同步”
created_at 和 updated_at 时间戳字段 + 触发器只能辅助识别变更,不能替代增量机制
加时间戳字段(如 updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP)配合触发器更新,确实能标记哪些行被改过,但仅此而已。它解决不了“怎么安全、可靠、可重试地把变更传出去”这个问题。
- 触发器可以自动维护
updated_at,但无法防止应用层绕过该字段直接UPDATE ... SET updated_at = '2020-01-01' - 如果业务允许“软删除”,你还需要额外字段(如
is_deleted TINYINT)并让触发器也响应它 - 单纯查
WHERE updated_at > '2026-04-17 00:00:00'做轮询,会漏掉刚插入又立刻更新的记录(时间精度不足)、遇到时钟回拨就乱套
真正可行的“基于触发器的日志表”模式:只写变更事件,不写业务数据
如果你必须用触发器作为起点,唯一较可控的做法是:建一张轻量级变更日志表(change_log),只存元信息,由独立服务去读、去投递、去确认。
- 日志表结构建议包含:
id BIGINT AUTO_INCREMENT、table_name VARCHAR(64)、pk_value VARCHAR(255)(主键值,支持复合主键拼接)、operation ENUM('INSERT','UPDATE','DELETE')、created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP - 触发器里只做最简操作:
INSERT INTO change_log (table_name, pk_value, operation) VALUES ('user', NEW.id, 'INSERT');—— 避免任何 JOIN、子查询、函数调用 - 外部消费者要按
id顺序读取,并在处理成功后记录已消费的id(不能依赖时间戳做断点) - 日志表需定期归档或清理,否则会无限膨胀;且必须为
id加索引,否则SELECT * FROM change_log WHERE id > ? ORDER BY id LIMIT 100会变慢
真正该用什么替代触发器做增量同步
生产环境应直接放弃“触发器+时间戳”这种 DIY 方案。2026 年还在这么干,等于主动给自己埋性能债和一致性坑。
- MySQL 自身的
binlog是唯一可靠的变更源,canal、maxwell、Debezium都基于它,支持事务完整性、位点追踪、断点续传 - 云厂商 DTS(如阿里云 DTS、华为云 DRS)已封装好全量+增量逻辑,兼容各种 DDL 变更,还能自动处理主从切换
- 如果只是同库内物化视图场景,可用
INSERT ... ON DUPLICATE KEY UPDATE配合定时任务,比触发器更可控
触发器适合做审计留痕、简单状态派生(如订单创建后自动设初始状态),不适合承担数据同步职责。一旦开始写 INSERT INTO remote_db.target ... 或在触发器里调用存储过程发 HTTP 请求,就已经踩进运维黑洞了。











