触发器中禁止使用SUM()、JOIN、子查询、跨表修改自身表等操作,应改用NEW/OLD伪记录、异步处理或应用层计算,避免性能崩塌与死锁。

触发器里别写 SUM()、JOIN 或子查询
触发器在行级操作时同步执行,一旦里面嵌套聚合或关联查询,每插入/更新一行就触发一次全表扫描或临时连接,性能直接崩。比如在订单表的 AFTER INSERT 触发器里去查用户累计消费,等于每次下单都跑一遍 SELECT SUM(amount) FROM order WHERE user_id = NEW.user_id —— 数据量一过万,延迟就明显。
- 把聚合逻辑移到应用层或定时任务里异步更新汇总字段(如用户表加
total_spent字段) - 如果必须实时,改用
BEFORE INSERT/UPDATE直接计算单行影响:比如插入新订单时,只做NEW.user_total := OLD.user_total + NEW.amount - 绝对避免在触发器里调用存储过程,尤其是带循环或游标的——MySQL 的触发器不支持事务回滚内部过程错误,容易数据不一致
MySQL 触发器中慎用 SELECT ... INTO
这个语句表面看只是查个值赋给变量,但实际会隐式开启一致性读(consistent read),在高并发写入场景下极易引发锁等待甚至死锁。尤其当目标表本身就在被主 SQL 修改时,触发器再读它,InnoDB 很可能卡住。
- 优先用
NEW和OLD伪记录取值,它们是内存快照,零开销 - 真要查外部状态,确保查询条件命中主键或唯一索引,且只查单行;否则加
LIMIT 1并显式加FOR UPDATE(但需评估锁粒度) - 测试时重点看
SHOW ENGINE INNODB STATUS输出里的TRANSACTIONS部分,是否有长时间等待lock_mode X locks rec but not gap waiting
PostgreSQL 中用 pg_notify() 替代重逻辑触发器
PG 的触发器函数虽然支持 PL/pgSQL,但复杂逻辑仍会阻塞 DML。与其在触发器里处理消息推送、缓存刷新、统计更新,不如只发个通知,让外部消费者(如 Python worker 或 Node.js 进程)异步承接。
- 触发器内只写:
PERFORM pg_notify('order_events', json_build_object('id', NEW.id, 'action', 'insert')::text); - 监听端用
LISTEN order_events持久连接接收,避免轮询开销 - 注意
pg_notify()不保证送达,关键业务需搭配数据库表记录事件日志做幂等补偿
触发器里修改同一张表会报 Can't update table 'xxx' in stored function/trigger
MySQL 明确禁止触发器中对当前表执行 INSERT/UPDATE/DELETE,这是硬限制,不是配置问题。常见于想“自动补全”字段或“联动更新状态”,结果直接报错。
- 用
BEFORE触发器直接改NEW.xxx值,而不是发一条UPDATE语句 - 跨表联动可以,但目标表不能是触发器所属表;例如订单表触发器更新用户表可以,更新订单表自身不行
- 真需要自更新(比如生成流水号),改用应用层生成后传入,或用序列+默认值+唯一约束兜底
最常被忽略的是:触发器性能问题往往不是慢在 SQL 本身,而是慢在它把原本可批量的操作,强行拆成单行同步执行。只要逻辑能挪出触发器,哪怕多一次网络往返,也大概率更快更稳。










