触发器在批量INSERT时极慢,因其强制将一次物理写入拆为N次同步逻辑执行,导致BULK INSERT退化为伪批量;禁用触发器是比优化更有效的安全路径。

触发器在批量 INSERT 时极慢,不是配置没调好,而是它根本就不是为这个场景设计的——AFTER INSERT 触发器会把本该一次完成的物理写入,强行拆成 N 次同步逻辑执行。
触发器让 BULK INSERT 退化成“伪批量”
SQL Server 的 BULK INSERT 和 SqlBulkCopy 原本能跳过常规执行路径、用最小日志模式写入,前提是不触发任何行级逻辑。一旦目标表上有 AFTER INSERT 触发器:
- 引擎必须构造完整的
inserted表(无统计信息、无法走索引),哪怕只插 10 万行,也得为每一行做一次触发器上下文初始化 -
inserted表数据无法被优化器预估,触发器内所有JOIN或WHERE都极易退化为全表扫描 - 隐式事务被强制开启,
TABLOCK和BULK_LOGGED恢复模式的日志优化全部失效 - 原本 200MB 的物理写入,因触发器额外写审计表/更新缓存表,WAL 日志可能暴涨到 800MB+,
WRITELOG等待飙升
MySQL 下同样逐行执行,且更难诊断
MySQL 不支持 BULK INSERT 语法,但 INSERT INTO t VALUES (),(),... 或 INSERT ... SELECT 批量语句一样逃不过触发器逐行调用:
-
NEW.created_at = NOW()这种简单赋值,10 万行就是 10 万次系统时钟调用,在高并发下延迟直接累积成秒级 -
BEFORE INSERT里查关联表?只要accounts.user_id没索引,每次插入都全表扫描 + 加锁,其他线程全卡在Sending data - 想用
performance_schema定位耗时?得先确认events_stages_history已启用,再查TIMER_WAIT(单位是皮秒,别当毫秒读) - MySQL 没有 SQL Profiler,别指望点开就能看触发器火焰图——这是最常踩的起点错误
禁用比优化更有效,且有安全路径
给触发器加索引、拆函数、缓存查询结果,本质是在修一个不该存在的瓶颈。真正有效的做法是绕过它:
- SQL Server:执行
DISABLE TRIGGER trigger_name ON table_name,批量完再ENABLE;注意权限需ALTER表级权限 - MySQL 8.0.19+:支持
ALTER TABLE t1 DISABLE TRIGGER tr_log,比删触发器安全,上线前可灰度验证 - 真要保审计一致性?改用
INSERT ... SELECT+ 中转表,或让应用层发 MQ,避免把异步逻辑硬塞进事务 - 字段自动填充(如
updated_at)优先用生成列:updated_at DATETIME AS (NOW()) STORED,不走触发器路径
最容易被忽略的一点:即使触发器逻辑为空(只写 SELECT 1),它仍会强制事务串行化,阻塞并发写入。性能问题不在“怎么写”,而在“该不该在这里写”。

















