必须禁用触发器,因mysql批量导入时触发器默认逐行执行,10万行即10万次调用,开销源于频次而非逻辑复杂度;应通过重命名触发器等运维手段临时禁用,而非优化其内部逻辑。

批量导入时触发器默认逐行执行,10 万行就是 10 万次逻辑调用——这不是配置问题,是 MySQL 的设计行为。最直接有效的做法是导入前禁用、导入后启用。
批量导入前必须禁用触发器
MySQL 不支持 DISABLE TRIGGER 语法(SQL Server/PostgreSQL 支持),但等效操作是:ALTER TABLE t DISABLE KEYS 不管用,真正有效的是用 SET @trig_disabled = 1 配合条件判断,或更稳妥的运维手段:
- 临时重命名触发器(需有
DROP TRIGGER权限):RENAME TRIGGER tg_old TO tg_old_disabled - 导出时跳过触发器定义:
mysqldump --skip-triggers db table > dump.sql - 应用层控制:在导入脚本开头执行
SET SESSION sql_log_bin = 0(仅限主库且 binlog 不关键时)
注意:DISABLE TRIGGER 在 MySQL 中不存在;别信网上抄来的错误语句,否则会报 ERROR 1064。
为什么不能靠“优化触发器逻辑”来救批量导入
即使触发器只做 NEW.updated_at = NOW() 这种赋值,在 10 万行批量插入中仍会产生 10 万次上下文切换和执行调度开销。性能瓶颈不在逻辑复杂度,而在执行频次本身。
- BEFORE 触发器会延长事务持有行锁的时间,加剧并发等待
- AFTER 触发器若含写日志表操作,会导致每行都触发一次 WAL 写入,I/O 直接翻 10 万倍
- MySQL 8.0+ 对触发器内锁行为更严格,原本 5.7 没暴露的死锁,在 8.0 可能直接报
Deadlock found when trying to get lock
想靠加索引、改函数、拆逻辑来“提速”触发器应对批量导入,本质是方向错了。
替代方案比“修触发器”更可靠
真正适合批量场景的不是“让触发器跑得更快”,而是绕过它,把逻辑移到更可控的位置:
- 用
LOAD DATA INFILE导入原始数据,再用单条UPDATE ... JOIN或INSERT INTO summary SELECT ... GROUP BY做后置聚合 - 审计类需求改用生成列(
GENERATED COLUMN)或应用层埋点,避免运行时计算 - 必须同步更新的字段(如状态码标准化),改在应用拼 SQL 时显式传入:
INSERT INTO t (status, normalized_status) VALUES ('P', 'pending'),触发器只留空壳或直接删掉
触发器不是万能胶水,它是事务的一部分,天然不适合吞吐优先的批量场景。越早接受这点,越少在慢日志里看到 TRIGGER 关键字。











