触发器执行慢主因是隐式类型转换致索引失效,需用explain检查执行计划并确保字段类型严格一致;insert触发器不可修改自身表,应改用应用层补偿;调用存储过程需防参数错误和命名冲突;高并发下触发器易锁表,应避免访问大表或复杂操作。

触发器执行慢,先查它有没有隐式类型转换
SQL 触发器卡顿,90% 不是逻辑复杂,而是 WHERE 条件或 JOIN 字段类型不一致,导致索引失效。比如在 INSERT 触发器里写 SELECT * FROM orders WHERE user_id = NEW.profile_id,而 orders.user_id 是 BIGINT、NEW.profile_id 是 VARCHAR —— MySQL 会悄悄把整列转成字符串比对,全表扫描就来了。
- 用
EXPLAIN检查触发器内每条语句的执行计划,重点看type是否为ALL或index - 确保触发器中所有比较字段类型严格一致,必要时显式加
CAST(... AS ...) - 避免在触发器里调用函数做条件判断,如
DATE(created_at) = CURDATE(),这类写法无法走索引
INSERT 触发器里改同一张表,MySQL 会报错
在 INSERT 触发器里再对当前表执行 UPDATE 或 INSERT,MySQL 直接抛出 ERROR 1442 (HY000): Can't update table 'xxx' in stored function/trigger because it is already used by statement which invoked this stored function/trigger。这不是配置问题,是引擎层硬限制。
- 改用应用层补偿:触发器只记录变更意图(写入日志表),由后台任务异步处理
- 如果必须同步更新,把逻辑移到应用代码里,别塞进触发器
- PostgreSQL 没这个限制,但同样不建议这么做——容易形成递归或死锁
触发器里调用存储过程,参数传错会导致静默失败
MySQL 触发器调用存储过程时,如果参数个数或顺序不对,不会报语法错误,而是按默认值填充,结果就是过程没干该干的事,还查不出原因。
- 调用前用
SELECT打印关键变量值,例如SELECT CONCAT('user_id:', NEW.user_id, ', status:', NEW.status); - 存储过程参数名别和
NEW/OLD字段重名,否则可能被意外覆盖 - 避免用
INOUT参数传值进触发器——MySQL 对它的处理不稳定,尤其在批量插入时
并发高时触发器锁表,比想象中更严重
哪怕只是 INSERT ... SELECT 读一行数据,触发器也可能持锁到事务结束。线上高峰期,一个慢触发器会让整个表的写入排队,SHOW PROCESSLIST 里全是 Waiting for table metadata lock。
- 触发器内禁止访问大表、禁止
ORDER BY ... LIMIT类操作(易触发临时表+排序) - 用
SELECT ... FOR UPDATE要格外小心,它会升级行锁为间隙锁,影响范围远超预期 - 测试时别只看单条 SQL 响应时间,压测得模拟真实并发量,观察
Innodb_row_lock_waits是否飙升
触发器不是黑盒,它和主 SQL 共享事务上下文、锁资源、执行线程。写之前想清楚:这事非得在数据库里做不可吗?











