触发器在批量更新时必然拉低性能,因其将一次批量操作拆为n次串行事务;优化应绕过而非改造触发器,如用insert...on duplicate key update、生成列或应用层处理替代。

触发器在批量更新时必然拉低性能,不是“可能慢”,而是“一定慢”——它把一次批量操作硬拆成 N 次串行事务,锁、日志、CPU 全被绑死。优化触发器不如绕过它,禁用或替代才是有效解法。
为什么UPDATE触发器会让10万行更新变80秒?
MySQL 对 UPDATE t SET x=1 WHERE id IN (1,2,...,10000) 不会整体触发一次,而是对每一行调用一次触发器。哪怕只有一行 NEW.updated_at = NOW(),高并发下系统时钟函数争用也会累积延迟;若含 SELECT 或自定义函数,单次开销从 0.3ms 拉到 8ms+,10 万行就是 80 秒。
- 常见错误现象:
SHOW PROCESSLIST中大量线程卡在Updating状态 -
slow_query_log里同一条UPDATE反复出现,每次耗时稳定几毫秒以上 -
INFORMATION_SCHEMA.PROFILING显示触发器逻辑占总耗时 70%+ -
innodb_row_lock_time_avg突增,但慢日志里找不到对应 SQL(触发器耗时不单独记录)
如何让SQL不走触发器执行路径?
真正有效的办法是让语句本身绕过触发器,而不是给触发器加索引或拆逻辑。
- 用
INSERT ... ON DUPLICATE KEY UPDATE替代UPDATE:先把目标数据写入临时表,再用该语法完成,全程不触发任何触发器 - 把
updated_at这类字段改用生成列:updated_at DATETIME AS (NOW()) STORED(MySQL 8.0+),写入时自动计算,不经过触发器 - 审计字段如
created_by必须由应用层显式传参,不要依赖@user_id或触发器读取 session 变量 - 统计类变更(如订单数累加)彻底移出 DB,改用 BINLOG 解析(
Canal/Maxwell)或应用层异步任务
必须保留触发器时的底线约束
如果因强审计要求无法移除(例如金融系统),那就只允许最简化的 BEFORE 触发器,且体内严禁任何副作用。
- 仅支持
BEFORE INSERT和BEFORE UPDATE,禁用所有AFTER类型 - 体内禁止任何
SELECT、UPDATE、INSERT、DELETE操作 - 禁止调用
READS SQL DATA或MODIFIES SQL DATA的自定义函数 - 字段赋值仅限确定性表达式,例如
NEW.ts = UNIX_TIMESTAMP(),不用NOW() - 禁止
IF/CASE分支以外的逻辑分支(比如循环、异常捕获)
哪怕你用了 LIMIT 分批执行 UPDATE,只要表上有触发器,每一批里的每一行仍会触发一次——批次越小,上下文切换开销越重。别指望 EXPLAIN 能看出触发器耗时,它根本不进执行计划。











