MySQL触发器执行慢的主因是内部SELECT查询,应移至应用层预查;避免复杂逻辑、重复触发器及高并发函数调用,优先用CDC或应用层异步替代。

触发器里别写 SELECT 查询
MySQL 触发器执行慢,最常见原因是触发器内部做了 SELECT 查询,尤其是关联多表或没走索引的查询。触发器在事务中同步执行,SELECT 会加锁、阻塞写入,还可能引发死锁。
实操建议:
- 把触发器里的
SELECT拆出来,改由应用层在 INSERT/UPDATE 前查好数据,再一并传入 - 如果必须查,确保被查字段有覆盖索引,且查询条件能命中索引(比如用主键或唯一键,别用
LIKE '%xxx') - 避免在
BEFORE INSERT里查刚要插入的同一张表——MySQL 不允许(报错Can't update table 't' in stored function/trigger),但AFTER INSERT查自己也可能导致递归或性能抖动
触发器逻辑尽量只做简单赋值和判断
触发器不是业务逻辑容器。像调用存储过程、拼接 JSON、计算复杂表达式、写日志表(尤其没索引的日志表)、发 HTTP 请求等操作,都会显著拖慢 DML 速度。
实操建议:
- 把时间敏感的逻辑(如更新统计字段)保留为简单
SET NEW.xxx = ...;非关键逻辑(如记录变更快照)移到应用层异步处理 - 如果要用触发器记日志,确保日志表是
ENGINE=BLACKHOLE(仅丢弃)或至少有主键+合理索引,且写入语句不带子查询 - 避免在触发器里调用
UUID()、NOW(6)等函数——它们虽快,但高并发下可能成为争用点;优先用应用层生成
检查触发器是否被重复创建或误启用
开发过程中容易反复 CREATE TRIGGER 却没先 DROP,导致多个同名触发器残留(MySQL 允许多个同名触发器共存,只要 EVENT + TIME + TABLE 组合不同)。上线后这些“幽灵触发器”会静默执行,叠加耗时。
实操建议:
- 用
SHOW TRIGGERS LIKE 'table_name';查看当前所有触发器,确认数量和定义是否符合预期 - 用
SELECT * FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = 'db_name';排查重复或废弃的触发器 - 上线部署脚本中,统一用
DROP TRIGGER IF EXISTS trigger_name;+CREATE TRIGGER组合,避免累积
替代方案:用应用层钩子或 CDC 工具更可控
真正复杂的业务逻辑(比如跨库更新、调用外部服务、批量反查)根本不适合放在 MySQL 触发器里。它不可观测、难调试、无法降级,还会让数据库承担不该有的职责。
实操建议:
- 对强一致性要求不高场景:应用在事务提交后发消息到 Kafka/RocketMQ,由下游消费处理,解耦且可重试
- 对强一致性要求高场景:用 Debezium 或阿里 canal 订阅 binlog,实现变更捕获(CDC),比触发器更轻量、无侵入、支持水平扩展
- 临时排查可用
SET profiling = 1;+SHOW PROFILES;定位某条 INSERT 耗时是否真卡在触发器,而不是网络或锁等待
SELECT EVENT_OBJECT_TABLE, ACTION_STATEMENT, CREATED FROM information_schema.TRIGGERS WHERE TRIGGER_SCHEMA = 'mydb' AND EVENT_MANIPULATION = 'INSERT';
触发器优化的本质,是承认它只是数据库的“缝合补丁”,不是业务逻辑的主干。越想让它干得多,系统就越难维护和伸缩。











