mysql触发器中select查不到本事务刚插入的数据,是mvcc正常行为;应使用new/old引用字段,跨行校验需用after触发器+显式事务;on duplicate key update仅触发update类触发器;避免触发器调用写表存储过程以防死锁;审计日志触发器须建索引并禁用拼接操作。

触发器里写 SELECT 还没提交就查到了脏数据?
MySQL 的 BEFORE 触发器里执行 SELECT,查的其实是事务当前快照里的旧值——哪怕你刚在同个事务里 INSERT 了一条记录,SELECT 也看不到它。这不是 bug,是 MVCC 的正常行为。
- 想查刚插入/更新的数据,得用触发器参数:
NEW.col_name或OLD.col_name,别去查表 - 如果真要跨行校验(比如检查用户名是否重复),必须用
AFTER触发器 + 显式事务控制,否则可能漏判 - PostgreSQL 没这问题,它的
BEFORE触发器能看到本事务已做的变更,但 MySQL 不行
INSERT ... ON DUPLICATE KEY UPDATE 和触发器打架怎么办?
当语句命中唯一键冲突时,MySQL 会走 UPDATE 分支,但只触发 BEFORE UPDATE 和 AFTER UPDATE,完全跳过 INSERT 类触发器。很多人误以为“既然写了 INSERT 就该进 INSERT 触发器”,结果逻辑断层。
- 把核心校验或生成逻辑从
BEFORE INSERT搬到BEFORE UPDATE,或者两个都写一遍 - 避免在触发器里依赖“这是 INSERT 还是 UPDATE”的判断,改用
IF NEW.id = OLD.id THEN ...这类实际字段比对 - 注意
ON DUPLICATE KEY UPDATE中的VALUES(col)函数返回的是原始 INSERT 值,不是最终写入值,别拿它在触发器里做状态推导
触发器调用存储过程导致死锁?
触发器里调用含 UPDATE 或 INSERT 的存储过程,极易引发死锁,尤其在高并发批量写入时。因为触发器运行在原 SQL 的事务上下文中,锁粒度和顺序全被带进去了。
- 优先把逻辑平铺进触发器体,去掉存储过程调用;如果必须复用,确保该过程只读、不碰任何用户表
- 避免在触发器里调用另一个会修改同张表的存储过程——等于自己锁自己
- MySQL 8.0+ 可用
GET_LOCK()做轻量级互斥,但别在触发器里用,会放大阻塞
审计日志触发器为什么越跑越慢?
给每张业务表配一个 AFTER INSERT/UPDATE/DELETE 触发器,往 audit_log 表里插记录,初期没问题,半年后单次插入延迟从 2ms 涨到 200ms,根本原因是日志表没索引 + 触发器没批处理。
-
audit_log至少建复合索引:(table_name, op_time),否则SELECT COUNT(*)类统计查询会拖垮整个触发器链 - 别在触发器里做
CONCAT拼大 JSON 字段,字符串操作在行级触发器里开销极大;用JSON_OBJECT()更稳 - 真正大批量操作(如迁移、补数)前,临时
DROP TRIGGER,事后补日志,别硬扛
触发器不是黑盒魔法,它和主 SQL 共享事务、锁、执行计划——所有你以为“它在后台悄悄干”的事,其实都卡在你当前这条语句的路径上。










