触发器在批量insert中逐行执行,导致性能严重下降;应禁用触发器或改用on duplicate key update、生成列等替代方案,仅在必要时保留纯计算型before触发器。

触发器在批量INSERT里被逐行执行,根本停不下来
MySQL 对 INSERT INTO t VALUES (...), (...), ... 这类语句,不会“整体触发一次”,而是对每一行调用一次触发器。哪怕触发器只有一行 NEW.created_at = NOW(),10 万行就是 10 万次系统时钟调用——高并发下微小延迟会累积成秒级卡顿。
常见错误现象包括:SHOW PROCESSLIST 中大量线程卡在 Updating 或 Writing to net 状态;慢查询日志里同一条 INSERT 反复出现,每次耗时稳定在几毫秒以上;INFORMATION_SCHEMA.PROFILING 显示触发器逻辑占总耗时 70%+。
- 触发器内任何表访问(哪怕
SELECT COUNT(*) FROM config)都会让单行开销从 0.2ms 拉到 5ms+,10 万行就是 500 秒 -
NEW/OLD表无法走索引,INSERT ... SELECT场景下触发器里的JOIN极易退化为全表扫描 - SQL Server 的
inserted表无统计信息,MySQL 8.0 前也不支持对NEW做索引提示
触发器让 WAL 和 IO 压力翻倍
批量插入本应是一次性物理写入,但触发器把它拆成 N 次逻辑写 + 日志写 + 锁等待。原始插入 50 万行生成约 50MB WAL,若触发器再同步更新 2 张审计表,每行写 100 字节日志,额外增加 100MB WAL —— checkpoint 频率被迫升高,I/O 带宽被日志抢占。
验证方法:sys.dm_io_virtual_file_stats(SQL Server)或 performance_schema.events_statements_history_long(MySQL)中查含 TRIGGER 的事件,看平均执行时间是否随批量增大而线性上升。
- SQL Server 中
wait_info频繁出现WRITELOG或IO_COMPLETION - MySQL 下
innodb_file_per_table=ON时,触发器还会产生大量.ibd文件碎片写入 - 临时禁用触发器跑同样批量:用
DISABLE TRIGGER trigger_name ON table_name,对比io_stall_write_ms是否骤降
禁用比优化更有效,因为设计定位本就不支持批量
MySQL 触发器的设计定位是“轻量、确定、单行响应”,不是“业务协调中枢”。很多团队花时间加索引、拆函数、缓存查询结果,但问题根源不在写得不够好,而在于它本就不该承担批量场景下的逻辑分发职责。
真正绕过触发器的写法:INSERT ... ON DUPLICATE KEY UPDATE、INSERT ... SELECT、LOAD DATA INFILE —— 这些语句本身不显式触发 UPDATE,就不会走触发器路径。
- 字段自动填充(如
updated_at)改用生成列:updated_at DATETIME AS (NOW()) STORED(MySQL 8.0+) - 审计字段(如
created_by)必须由应用层显式传参,别依赖@session_var或触发器读取连接上下文 - 统计类变更(如订单数累加)彻底移出 DB:用 BINLOG 解析(Canal/Maxwell)监听变更,避免和主 DML 绑定在同一事务
必须保留触发器时,只允许 BEFORE + 纯计算
如果因合规或历史原因无法移除(比如金融系统强审计要求),那就把触发器压缩到只剩最底线能力:仅 BEFORE INSERT/BEFORE UPDATE,且体内禁止任何表访问、函数调用、条件分支外的副作用。
例如允许:SET NEW.updated_at = NOW();;不允许:SELECT SUM(amount) FROM order WHERE user_id = NEW.user_id; 或 CALL calc_score(NEW.id);。
- 禁止在触发器里写
UPDATE其他表,否则锁粒度放大 + 日志暴涨 + 执行计划重编译三重叠加 - 禁止游标:每次触发都重新编译、结果集全加载进内存、
FETCH必须串行等待,极易引发Waiting for table metadata lock - 监控指标如
Created_tmp_disk_tables突增、memory/sql/THD::main_mem_root占用飙升,基本就是游标在吃内存
EXPLAIN 看不到它,但 sys.dm_exec_trigger_stats 或 performance_schema 一查就露馅。最容易被忽略的是——你以为在优化 SQL,其实是在给一个本不该存在的执行路径打补丁。










