MySQL触发器执行慢拉高CPU主因是“算得多”而非“写得多”,因其同步执行子查询、JOIN、函数等重型逻辑且支持递归触发,需禁用递归、精简逻辑、移出重操作至应用层。

触发器执行慢直接拉高CPU,不是因为“写得多”,而是“算得多”
MySQL触发器本身不占用常驻资源,但每次INSERT/UPDATE/DELETE触发时,都会同步执行其定义的SQL逻辑。如果触发器里包含子查询、多表JOIN、函数调用(比如UUID()、NOW())、或嵌套INSERT INTO ... SELECT,CPU就会在单次事务中被反复压榨。更危险的是——这些操作都在主SQL的事务上下文中执行,无法异步,也无法被慢查询日志完整捕获。
递归触发是CPU飙升的隐形加速器
MySQL默认允许触发器间接引发其他触发器(比如A表INSERT触发B表UPDATE,而B表的UPDATE又触发了另一个触发器),只要没显式禁用log_bin_trust_function_creators或没设max_sp_recursion_depth,就可能形成链式反应。一个看似简单的INSERT可能实际执行了几十次嵌套查询,每层都走一遍解析、优化、执行流程,CPU自然飙到300%+。
- 检查是否开启递归:
SELECT @@max_sp_recursion_depth;,值为0表示不限制(最危险) - 临时禁止递归(会话级):
SET max_sp_recursion_depth = 1;,只允许1层触发 - 查当前活跃触发器调用栈:没有直接命令,但可通过
SHOW PROCESSLIST里info字段看到正在执行的触发器语句,再结合INFORMATION_SCHEMA.TRIGGERS反查定义
优化触发器逻辑的实操要点
核心原则:触发器只做“轻量、确定、必要”的事。所有需要扫描、聚合、远程调用、复杂判断的逻辑,都应该挪到应用层或定时任务里。
- 删掉触发器里的
SELECT ... FROM other_table WHERE ...——改用应用层查好ID后传入;如必须查,确保other_table有覆盖WHERE条件的联合索引 - 避免在触发器里调用自定义函数,尤其是含SQL语句的函数;
DETERMINISTIC函数也慎用,MySQL有时会误判其开销 - 用
NEW.column_name代替(SELECT column FROM table WHERE id = NEW.id),后者强制回表,哪怕有索引也多一次B+树查找 - 批量操作(如
INSERT INTO t VALUES (),(),())会逐行触发,若业务允许,改成应用层合并逻辑,用单条带CASE WHEN的UPDATE替代多个触发器更新
容易被忽略的坑:触发器 + 事务 + 长连接
一个长事务里执行了100次带触发器的UPDATE,触发器每次又去查日志表、更新统计表……这些操作全锁在同一个事务里,不仅CPU高,还会拖慢整个事务的提交速度,进而阻塞其他连接。这时SHOW ENGINE INNODB STATUS里能看到大量waiting for table metadata lock或updating状态线程,但根源不在锁本身,而在触发器把简单操作变成了重型计算。
真正难排查的,是那些没报错、不写慢日志、EXPLAIN还显示“Using index”的触发器——它快在单次,慢在叠加。上线前务必用真实数据量压测触发路径,别信开发环境那几条测试数据。


















