最直接有效的做法是导入前禁用、导入后启用:mysql用alter table t disable trigger tg_name;postgresql用alter table t disable trigger tg_name;sql server用disable trigger tg_name on t。

批量INSERT时触发器被调用N次,怎么停?
MySQL、SQL Server、PostgreSQL 的行级触发器在 INSERT INTO t VALUES (),(),() 中默认对每一行执行一次——10 万行就是 10 万次触发逻辑。这不是配置问题,是设计行为。
最直接有效的做法:导入前禁用,导入后启用。
- MySQL:
ALTER TABLE users DISABLE TRIGGER tg_user_audit,完成后ENABLE TRIGGER - PostgreSQL:
ALTER TABLE users DISABLE TRIGGER tg_user_audit - SQL Server:
DISABLE TRIGGER tg_user_audit ON users
注意:DISABLE TRIGGER 不影响约束和索引,只停触发逻辑;它不阻塞写入,也不需要锁表(PostgreSQL/SQL Server 支持并发 DML),比改代码或加缓存快得多。
触发器里写了UPDATE或INSERT,为什么死锁?
在 AFTER INSERT ON orders 触发器里再 UPDATE orders,等于让同一事务反复尝试获取同一张表的行锁——极易触发死锁,且 PostgreSQL 会报 ERROR: stack depth limit exceeded(递归超限),MySQL 可能卡在 Waiting for table metadata lock。
正确做法只保留纯计算或单向写入:
- BEFORE 触发器中仅赋值:
SET NEW.updated_at = NOW(),不查表、不更新其他行 - AFTER 触发器中只写审计表或发消息,目标表必须与被触发表不同
- 绝对避免在触发器里调用
SELECT ... FOR UPDATE或任何带锁的语句
如果业务真要联动更新,改用 MERGE(SQL Server)、INSERT ... ON CONFLICT(PostgreSQL)或应用层统一处理,别交给触发器。
想用触发器做统计汇总,但批量插入慢得像卡住
逐行触发 + 每次 UPDATE summary SET count = count + 1 WHERE status = NEW.status,会导致 1000 行插入带来 1000 次索引查找、1000 次行锁、1000 次 WAL 写入——I/O 和锁开销爆炸。
PostgreSQL 有解:用 FOR EACH STATEMENT + NEW TABLE 一次性聚合:
CREATE OR REPLACE FUNCTION tg_summary_batch() RETURNS TRIGGER AS $$
BEGIN
INSERT INTO summary_cache (status, cnt)
SELECT status, COUNT(*) FROM NEW TABLE GROUP BY status
ON CONFLICT (status) DO UPDATE SET cnt = summary_cache.cnt + EXCLUDED.cnt;
RETURN NULL;
END;
$$ LANGUAGE plpgsql;
MySQL 没 NEW TABLE,只能异步解耦:触发器只写轻量 binlog 事件(如 INSERT INTO trigger_log (table_name, op, row_id) VALUES ('orders', 'INSERT', NEW.id)),再用 Canal 或 Debezium 消费后批量更新汇总表。
触发器里调了函数或查了配置表,为什么越插越慢?
SELECT value FROM config WHERE key = 'tax_rate' 这类查询在每行触发时都执行,无法走索引(NEW 表无统计信息)、不能复用执行计划、还会拖慢 WAL 刷盘节奏。
优化方向很明确:
- 把常量配置提到触发器外,用
DECLARE tax_rate NUMERIC := 0.08;(PG)或变量赋值(SQL Server)硬编码或预加载 - 避免在触发器里调用 UDF(用户定义函数),尤其含 I/O 或循环的;MySQL 8.0+ 的内置函数(如
JSON_EXTRACT)相对安全,但依然比直接字段操作慢 - 确认触发器是否真必要——字段自动填充优先用生成列:
updated_at DATETIME AS (NOW()) STORED(MySQL 8.0+),审计日志改用应用层埋点或 CDC 工具
触发器不是业务逻辑中台,它只是“最后一道轻量钩子”。一旦你发现要在里面 JOIN、聚合、远程调用或兜底容错,说明它已经超载了。











