AFTER UPDATE触发器能获取OLD值,它代表变更前整行且只读;常见误解源于表名错误、字段类型不匹配或误在BEFORE中修改OLD导致逻辑异常。

为什么 AFTER UPDATE 触发器拿不到 OLD 值?
能拿到。AFTER UPDATE 触发器完全支持 OLD 和 NEW 伪记录,这是 MySQL 的标准行为。常见误解源于把触发器写在了错误的表上,或误用了 BEFORE UPDATE 时对 OLD 的修改干扰了日志逻辑——但 OLD 在 AFTER 阶段仍是只读且完整的。
如何用 AFTER UPDATE 触发器安全记录旧值
关键不是“能不能”,而是“怎么记才不漏、不错、不拖慢主业务”。重点在日志表设计和触发器体内的字段选择:
- 日志表必须包含足够字段:如
log_id、table_name、row_id(原表主键值)、old_data(JSON 格式存储所有OLD字段)、updated_at、updated_by(需额外传入,MySQL 触发器本身不提供当前用户) - 避免在触发器里调用函数或查表,尤其是跨库查询;否则会阻塞主更新事务
- 不要用
CONCAT拼接所有字段——类型不一致易出错;改用JSON_OBJECT()(MySQL 5.7+)更可靠 - 如果原表主键是复合键,
row_id字段建议用VARCHAR(512)存 JSON 编码后的键值对
示例触发器体:
INSERT INTO user_log (table_name, row_id, old_data, updated_at)
VALUES ('user', OLD.id, JSON_OBJECT('name', OLD.name, 'email', OLD.email, 'status', OLD.status), NOW());
OLD 值在 AFTER UPDATE 中被意外清空或为 NULL?
这通常不是 MySQL 的问题,而是触发器定义有误:
- 触发器绑定的表名拼错,比如写了
user_logs却在user表上建触发器,导致OLD实际来自错误上下文 - 在
BEFORE UPDATE触发器中修改了OLD字段(语法上不允许,但若误写成赋值语句,MySQL 会静默忽略并可能破坏后续逻辑) - 原表某字段定义为
TEXT或BLOB,而日志表对应字段类型太小,插入时被截断,看起来像 NULL - 触发器中用了
IF OLD.col IS NULL判断,但该字段允许 NULL 且确实为 NULL,不是异常
性能与事务一致性要注意什么
AFTER UPDATE 触发器和主 DML 在同一事务内提交或回滚,这点很关键——但代价是锁等待时间变长:
- 日志表必须和原表在同一个数据库、使用相同引擎(推荐 InnoDB),否则无法保证原子性
- 不要给日志表加过多索引,尤其别在
old_data(JSON 字段)上建全文索引,写入开销陡增 - 高并发更新场景下,日志表的自增主键可能成为热点,可考虑分表或用 UUID 前缀缓解
- 如果业务允许“最终一致”,更轻量的做法是用
BINLOG+ 解析工具(如 Maxwell、Canal),而非触发器
真正容易被忽略的是:触发器里不能用 SELECT ... FOR UPDATE 或任何显式加锁语句,MySQL 会直接报错 ERROR 1442 (HY000): Can't update table in stored function/trigger。


















