必须用after insert触发器做跨表插入,因为before insert阶段自增主键尚未生成,new.id为null,导致目标表插入失败;after阶段整行已落盘,new.id等自动生成字段全部就绪,可安全用于跨表操作。

必须用 AFTER INSERT 触发器做跨表插入,BEFORE INSERT 里读不到自增主键——NEW.id 是 NULL,目标表插入会失败。
为什么 INSERT 同步只能用 AFTER,不能用 BEFORE
自增主键在 BEFORE INSERT 阶段尚未生成,此时访问 NEW.id 返回 NULL 或直接报错。只有 AFTER INSERT 才能确保整行已落盘,NEW.id、NEW.created_at 等自动生成字段全部就绪。
-
BEFORE INSERT中尝试INSERT INTO log_table(user_id) VALUES(NEW.id)→ 目标表写入0或NULL,违反非空/外键约束 -
AFTER INSERT中同一条语句可安全使用NEW.id,且能拿到触发器外层 SQL 写入的完整值 - 若业务需在插入前校验或改写字段(如清洗手机号、设置默认状态),才用
BEFORE INSERT,但不能依赖NEW.id
AFTER UPDATE 里怎么安全更新另一张表
不能在触发器里写 UPDATE target_table SET x = y WHERE id = NEW.id —— MySQL 5.7+ 会直接报错 ERROR 1442 (HY000),因为目标表和源表被同一事务锁定。
- 唯一合法方式是用标量子查询给
NEW字段赋值,仅限BEFORE UPDATE:例如SET NEW.sync_status = (SELECT status FROM config WHERE key = NEW.config_key LIMIT 1) -
AFTER UPDATE只能执行对其他表的INSERT/UPDATE/DELETE,但要确保目标表无外键指向源表,否则可能死锁 - 子查询必须加
LIMIT 1,否则多行返回中断整个触发器;没命中时返回NULL,建议用IFNULL(..., 'default')包裹 - 高并发场景下,这种子查询会成为瓶颈,尤其关联表缺失索引时
DELETE 和条件同步的实操陷阱
OLD 在 DELETE 触发器中可用,但容易因外键或唯一索引冲突导致主 DML 回滚。
- 判断字段变更要用
IF OLD.status != NEW.status THEN,别写OLD.* != NEW.*—— 含NULL时恒为FALSE - 字符串比较前务必
TRIM(),避免空格干扰:TRIM(OLD.phone) != TRIM(NEW.phone) -
INSERT IGNORE和REPLACE INTO对于因唯一键冲突跳过的行,完全不触发任何触发器,这点极易漏测 - 目标表必须设计幂等性,比如用
(source_id, event_time)做联合唯一键,防止重复插入
触发器是同步阻塞执行,目标表写入失败(死锁、磁盘满、约束冲突)会导致原始 SQL 整体回滚;它没有重试机制,也不支持 COMMIT/ROLLBACK —— 所有逻辑都在同一个事务上下文中运行。











