高并发下复杂触发器必然拖垮tps,因其将毫秒级dml变为同步阻塞多表事务,引发不可控行锁、死锁及cpu飙升;空触发器也有0.1–0.3ms开销,且explain无法追踪其sql,定位困难。

高并发下复杂触发器会直接卡住写入链路,不是“可能慢”,而是必然拖垮TPS——它把原本毫秒级的INSERT/UPDATE变成同步阻塞的多表事务,锁等待、死锁、CPU飙升全在暗处发生。
触发器让行锁时间不可控
触发器逻辑被包裹在主DML事务中,只要它没执行完,InnoDB就一直持有该行(甚至间隙)的锁。哪怕只是加了条日志:INSERT INTO audit_log,若audit_log表没主键或没索引,就会触发全表扫描式写入,在64并发下QPS直接腰斩。
- BEFORE UPDATE里用
SELECT COUNT(*) FROM config查配置?每次更新都重查,O(1)操作变O(n) - AFTER INSERT里
UPDATE users SET last_order_time = NOW() WHERE id = NEW.user_id?主事务锁订单行,触发器又去锁users行,另一条路径反着来,立刻Deadlock found when trying to get lock - 所有锁等待时间都算进
innodb_row_lock_time_avg,但slow log里只显示INSERT INTO orders,根本看不出是触发器在拖
触发器内SQL无法被EXPLAIN追踪
EXPLAIN INSERT INTO orders完全不展示触发器里的任何语句,你优化了主SQL,却对触发器内三条嵌套SELECT一无所知。想定位哪一句慢?只能靠performance_schema.events_statements_history_long,但SQL_TEXT字段默认截断,长逻辑得人工拼接。
-
SHOW PROCESSLIST只显示Updating或Executing trigger,不标出具体触发器名 - 错误信息如
ERROR 1422(“不允许提交”)从不指出是哪个触发器、哪一行代码引发的 - 想确认某张表有没有触发器?得先跑
SELECT * FROM INFORMATION_SCHEMA.TRIGGERS WHERE EVENT_OBJECT_TABLE = 'orders',再人工翻定义,没法自动化
并发测试时单条SQL压测完全失真
用mysqlslap -q "INSERT INTO orders..." -c 8测出来0.5ms,不代表真实场景——真正的瓶颈在锁排队。32线程持续压测5分钟,Innodb_row_lock_waits飙升、Threads_running卡在高位,才暴露触发器把事务串行化的真实代价。
- 空触发器也有0.1–0.3ms固定开销;一个含
SELECT ... FOR UPDATE的触发器,5ms就能让平均锁等待时间翻倍 - 批量
LOAD DATA INFILE时,触发器逐行调用,性能衰减呈线性增长,索引优化完全无效 -
SET SQL_LOG_BIN = 0可绕过binlog,但无法跳过触发器执行——它就是事务的一部分
最危险的不是触发器做了什么,而是它做了什么你根本看不见;最麻烦的不是它慢,而是你连它在哪慢都很难确认。一旦上线,它就成了数据库里一个不发声、不报错、只悄悄吃掉并发能力的黑盒。











