禁用触发器比优化更有效,因其在批量INSERT中必然逐行执行、无法并行——10万行即10万次调用,轻量逻辑亦致雪崩;应改用生成列、应用层传参及异步统计替代。

SQL触发器在批量插入时性能急剧下降,不是配置或写法问题,而是机制决定的——它必然逐行执行,10 万行就是 10 万次调用,再轻的逻辑也会雪崩。
为什么批量 INSERT 会让触发器变慢到不可接受
MySQL 对 INSERT INTO t VALUES (), (), ... 这类语句,不会整体触发一次,而是对每一行单独调用触发器。哪怕只有一行 NEW.created_at = NOW(),10 万行就是 10 万次系统时钟调用;高并发下微小延迟(如 0.5ms/行)会累积成秒级卡顿。
- 常见错误现象包括:
SHOW PROCESSLIST中大量线程卡在Updating或Writing to net状态 - 慢查询日志里同一条
INSERT反复出现,每次耗时稳定在几毫秒以上 -
INFORMATION_SCHEMA.PROFILING显示触发器逻辑占总耗时 70%+ - 触发器内任何表访问(哪怕
SELECT COUNT(*) FROM config)都会让单行开销从 0.2ms 拉到 5ms+,10 万行就是 500 秒
禁用触发器比优化更有效
MySQL 触发器的设计定位是“轻量、确定、单行响应”,不是“业务协调中枢”。加索引、拆函数、缓存结果都治标不治本——瓶颈不在怎么写,而在不该这么用。
- SQL Server:用
DISABLE TRIGGER [trg_name] ON [schema].[table],启用时用ENABLE TRIGGER - PostgreSQL(12+):会话级禁用,需在事务中执行
ALTER TABLE table_name DISABLE TRIGGER trg_name - MySQL:没有原生命令,可靠做法是重命名触发器:
RENAME TRIGGER `db.old_trigger` TO `db.old_trigger_disabled`,导入完再改回来 - 禁用后必须验证是否生效:执行
SELECT * FROM information_schema.TRIGGERS WHERE TRIGGER_NAME = 'xxx',确认EVENT_MANIPULATION字段未被跳过
真正绕过触发器的写法有哪些
让 SQL 本身不走触发器路径,才是根本解法。这些语句天然不激活行级触发器逻辑:
-
INSERT ... ON DUPLICATE KEY UPDATE:适用于有唯一键冲突场景,避免显式UPDATE触发 -
INSERT ... SELECT:整批构造数据再插入,不逐行触发 -
LOAD DATA INFILE:MySQL 原生高速导入,不走触发器(但注意:INSERT SELECT会触发) - 字段自动填充改用生成列:
created_at DATETIME AS (NOW()) STORED(MySQL 8.0+),完全绕过触发器 - 审计字段(如
created_by)必须由应用层显式传参,别依赖@session_var或CURRENT_USER()
统计类或跨表逻辑必须移出事务
触发器里做 UPDATE stats SET count = count + 1 或 INSERT INTO audit_log,会锁表、放大 WAL、拖慢主 DML。这不是慢,是设计错位。
- 统计类变更应解耦:用 BINLOG 解析(Canal / Maxwell)监听变更,异步更新
- 审计日志可先写本地文件或消息队列(Kafka / RabbitMQ),再由消费者聚合入库
- 临时应急可用
INSERT INTO log_tab SELECT ... FROM temp_table批量刷入,而不是每行插一次 - 切勿在触发器里写
SELECT ... FROM other_db.table——跨库查询无法复用事务上下文,连接开销翻倍
最常被忽略的一点:禁用触发器后,生成列和应用层传参必须同步补位,否则数据一致性就断了。很多人关了触发器却忘了改写入逻辑,结果字段全为 NULL 或默认值——性能上去了,业务却崩了。










