触发器无法实现强一致同步,仅支持写即同步;AFTER INSERT是唯一安全起点;UPDATE/DELETE需显式比对字段;INSERT IGNORE/REPLACE不触发触发器;跨表更新须用标量子查询。

不能靠触发器实现“强一致”同步——它最多做到“写即同步”,但不保证原子性、不可回滚、不防死锁,更无法跨库或兜底失败。 MySQL 5.7 的触发器本质是父事务的一部分,没有独立事务上下文,一旦目标表写入失败(如唯一键冲突、磁盘满、锁超时),整个原始 INSERT/UPDATE/DELETE 就直接报错回滚。这不是“同步失败”,而是“主操作根本没成功”。真要强一致,得用事务+应用层重试,或换 CDC 工具。
为什么 AFTER INSERT 是唯一安全的起点
想把 a_order_1 新增行实时写进 a_order_2,必须用 AFTER INSERT。因为:
-
BEFORE INSERT中NEW.order_id是NULL(自增主键还没生成),没法用来关联或写入目标表 -
AFTER INSERT才能拿到真实NEW.order_id,也才能确保源行已落盘、不会因后续逻辑中断而丢失 - 别信
LAST_INSERT_ID()—— 它在触发器里不可靠,尤其批量插入时返回值不确定
UPDATE 和 DELETE 触发器必须显式比对字段变更
直接无脑同步整行,会放大噪音和性能开销。比如时间戳字段每秒更新,AFTER UPDATE 就会每秒触发一次冗余写入。
- 只响应关键字段:用
IF OLD.status != NEW.status THEN ... END IF;判断业务状态是否真变了 - 注意
NULL比较失效:OLD.name != NEW.name在任一为NULL时结果为UNKNOWN,应改用NOT (OLD.name NEW.name)或TRIM(OLD.name) != TRIM(NEW.name) -
DELETE触发器只能读OLD.*,且必须用WHERE order_id = OLD.order_id精确删除,避免误删
INSERT IGNORE / REPLACE INTO 不触发任何触发器
这是最常踩的坑:你以为用了 INSERT IGNORE 就能跳过重复键并同步,其实它对跳过的行完全不走触发器逻辑。
面向 CRM 系统的 MySQL 8 数据库模式设计。当用户需要为客户关系管理(CRM)系统设计、评审、优化或生成数据库模式时,应使用此项技能。
-
INSERT IGNORE:冲突行不执行BEFORE INSERT或AFTER INSERT -
REPLACE INTO:本质是DELETE + INSERT,只触发BEFORE DELETE和BEFORE INSERT,中间状态丢失 - 正确姿势是统一用
INSERT ... ON DUPLICATE KEY UPDATE,它一定触发BEFORE INSERT,且能控制冲突后行为
跨表更新只能用标量子查询,不能 JOIN
想在触发器里根据另一张表(比如 config)动态设置某个字段值,语法上不允许 UPDATE t2 JOIN t1 或子查询多行返回。
- 必须写成单值子查询:
SET NEW.sync_status = (SELECT IF(is_valid=1,'ok','error') FROM config WHERE key_name = NEW.config_key LIMIT 1); -
LIMIT 1是硬性要求,漏写直接中断触发器执行 - 子查询每行都执行一次,如果
config表没建好索引,高并发下会成为瓶颈 - 查不到时返回
NULL,记得用COALESCE(..., 'unknown')设默认值
真正容易被忽略的点是:触发器里所有操作都在主表事务内完成,没重试、没日志、没幂等判断。哪怕只是往另一张本地表写一条日志,目标表锁住 2 秒,主表的 INSERT 就卡 2 秒。它不是同步方案,是耦合开关——开得越紧,崩得越快。










