触发器中查询大表缺失索引会严重拖垮qps;应在where、join、order by涉及字段建复合索引,避免在索引列上使用函数导致失效。

触发器里查大表没索引,直接拖垮QPS
这是最常见也最容易被忽略的性能雷区。比如在 AFTER INSERT 触发器里写 SELECT COUNT(*) FROM orders WHERE user_id = NEW.user_id,而 orders.user_id 没建索引,每次插入都触发一次全表扫描。
- 用
EXPLAIN直接执行触发器内 SQL,看type是否为ALL或index - 对所有
WHERE、JOIN、ORDER BY涉及的字段补索引,优先建复合索引(如查status = 'pending' AND created_at > NOW() - INTERVAL 1 DAY,建(status, created_at)) - 避免在索引列上用函数,
WHERE DATE(created_at) = '2026-08-04'会让索引失效,改用created_at >= '2026-08-04' AND created_at
BEFORE 和 AFTER 触发时机选错,锁时间翻倍
BEFORE 触发器能改 NEW 值,但若里面带 SELECT ... FOR UPDATE 或复杂校验,会延长主事务持锁时间;AFTER 不阻塞语句执行,但失败会导致整个事务回滚——两者代价完全不同。
- 需标准化字段(如清洗手机号)→ 用
BEFORE,但禁止任何跨表写入 - 只做状态标记、审计记录、轻量更新 → 用
AFTER,并在开头加IF NEW.status != OLD.status THEN ... END IF过滤无意义变更 - 批量操作前临时禁用触发器:
ALTER TABLE t DISABLE TRIGGER tr_name,处理完再启用
触发器调用存储过程,执行计划彻底失控
MySQL 对存储过程内 SQL 的执行计划缓存不敏感,尤其带参数的动态查询。触发器里 CALL update_user_stats(NEW.user_id),里面又查 logs 表,优化器可能反复生成低效计划。
- 把关键逻辑“提”回触发器本体(前提是不复用),让
EXPLAIN能真实反映路径 - 若必须用存储过程,在过程内显式指定索引:
SELECT * FROM logs FORCE INDEX (idx_user_id) WHERE user_id = p_user_id - 严禁拼接 SQL 字符串——预编译失效,执行计划无法复用
行级触发器在批量场景下被调用上万次
PostgreSQL/MySQL 默认 FOR EACH ROW,一次更新 10000 行,触发器函数就被调用 10000 次。上下文切换开销远超逻辑本身。
- 聚合类逻辑(如统计、汇总)优先改用
FOR EACH STATEMENT+ 过渡表(NEW TABLE/OLD TABLE) - MySQL 8.0+ 可用
INSERT ... ON DUPLICATE KEY UPDATE替代多行UPDATE触发 - 高频小更新场景,考虑把计数类逻辑前置到应用层(如 Redis 原子操作),绕过数据库触发器
真正卡住系统的往往不是触发器语法本身,而是某条没走索引的 SELECT、一次没控制深度的嵌套、或一个本该异步却硬塞进事务的 INSERT。优化时别只盯着触发器代码,得顺着 performance_schema 或慢日志,一层层揪出那条实际拖慢它的 SQL。










