禁用触发器是最简单有效的绕过方式,因行级触发器在批量导入时逐行执行,导致IO和事务开销线性甚至指数级放大;MySQL不支持语句级触发器,必须逐行执行,且无原生禁用语法,PostgreSQL用ALTER TABLE DISABLE TRIGGER ALL,SQL Server用DISABLE TRIGGER。
因为行级触发器在批量导入时会为每一行数据单独执行一次,把原本一次性的物理写操作拆成 N 次逻辑写、日志写和锁等待,IO 和事务开销呈线性甚至指数级放大。
MySQL 5.7/8.0 全系列不支持 FOR EACH STATEMENT 语法,所有触发器默认且唯一是行级。哪怕一条 INSERT INTO t SELECT ... FROM s 插入 10 万行,AFTER INSERT 触发器也会被调用 10 万次——每次都要初始化上下文、解析 SQL、写日志、加锁、等 WAL 刷盘。
BEFORE INSERT 里查关联表时,若条件字段没索引(如 WHERE email = NEW.email 但 email 无索引),每次插入都触发全表扫描AFTER INSERT 中再 UPDATE 同一张表,可能引发死锁或二次触发(尤其当触发器未设 SQL SECURITY DEFINER)批量导入本身已密集刷 WAL;触发器额外更新审计表、日志表,等于给日志系统叠加多层写压力。SQL Server 中 sys.dm_io_virtual_file_stats 显示 log_file 的 io_stall_write_ms 突增 10 倍以上,而 data_file 的 num_of_writes 却无明显变化——说明瓶颈卡在日志落盘,不是数据文件写不过来。
INSERT INTO audit_log,就多一次 mini-transaction + WAL 记录 + buffer pool 刷脏页机会.ibd 文件碎片,进一步拖慢随机写SELECT COUNT(*) FROM big_table 这类语句,会让 innodb_row_lock_time 和 innodb_buffer_pool_wait_free 同步飙升验证是否触发器导致变慢,最直接方式是临时禁用后重跑相同导入任务。PostgreSQL 用 ALTER TABLE t DISABLE TRIGGER ALL,SQL Server 用 DISABLE TRIGGER tr_name ON t。注意:必须在事务外执行,否则报错 The context in which the current operation is executing does not allow for transaction control。
sys.triggers(SQL Server)或 pg_trigger(PostgreSQL)确认触发器定义SET SQL_LOG_BIN = 0(主从场景慎用)或应用层绕过真正难处理的不是“怎么让触发器快一点”,而是“哪些业务逻辑非得塞进事务主路径”。一旦发现触发器里有跨库查询、HTTP 调用、大表 JOIN 或标量子查询,基本可以判定它不该存在——这些本该由异步队列或应用层统一收口。