触发器内查询常走全表扫描,因优化器无法获取new/old值的真实分布,估算失准且不复用主事务统计信息,叠加隐式转换、函数包装、索引顺序不当等因素,导致索引失效。

触发器里查其他表,为什么执行计划总走全表扫描?
因为触发器内 SELECT 语句的执行上下文受限,优化器无法复用主事务的统计信息或连接路径,常退化为孤立估算——尤其当查询条件含 NEW.xxx 或 OLD.xxx 时,MySQL/PostgreSQL 往往无法准确预估选择率,直接放弃索引走 Seq Scan。
- 触发器中所有
SELECT都是“单次、孤立”执行,不参与主 SQL 的联合执行计划生成 -
NEW.status这类值在编译期不可知,优化器只能按最坏情况(如 10% 选择率)估算,一旦实际数据倾斜(比如 95% 行 status=1),索引就失效 - 若查询字段类型与索引字段不一致(如
user_id INT关联log.user_id BIGINT),隐式转换让索引完全失效,EXPLAIN显示type: ALL
为什么 JOIN 多张表在触发器里特别危险?
触发器是行级同步阻塞执行,每插入/更新一行就完整跑一遍 JOIN 逻辑。哪怕只 JOIN 三张表,只要其中一张没走索引,就会变成嵌套循环 + 全表扫描,耗时随行数平方级增长。
PostgreSQL 18.4 官方 Ubuntu 安装包现已发布,这是目前最新的稳定版本。推荐通过官方 APT 仓库安装:先执行 sudo apt update 更新索引,再运行 sudo apt install postgresql-18 即可完成部署。新版本引入了异步 I/O 子系统,在顺序扫描与 VACUUM 场景下性能提升显著,同时支持 UUID v7 原生生成函数与虚拟生成列。
- MySQL 触发器中
SELECT a.*, b.name FROM orders a JOIN users b ON a.uid = b.id,若users.id无索引,每次触发都扫全表 - PostgreSQL 中
EXPLAIN (ANALYZE)会暴露真实瓶颈:出现Hash Join且Rows Removed by Filter占比超 80%,说明 WHERE 条件没走索引 - 别指望“只查一次”,1000 行批量插入 = 这个
JOIN执行 1000 次,不是 1 次
如何验证触发器内查询是否走了索引?
不能只看主 SQL 的 EXPLAIN,必须单独提取触发器里的 SELECT 语句,用真实值替换 NEW/OLD 后再分析。
- 把触发器中类似
SELECT count(*) FROM log WHERE status = NEW.status AND created_at > DATE_SUB(NOW(), INTERVAL 1 DAY)拆出来 - 手动代入一个典型值:比如
status = 'paid',然后执行EXPLAIN SELECT count(*) FROM log WHERE status = 'paid' AND created_at > '2026-06-23 00:00:00' - 重点看
key列是否非NULL,rows是否接近实际匹配行数——如果rows是表总行数的 80%,基本就是没走索引 - 复合索引必须满足最左前缀:
INDEX(status, created_at)有效,但INDEX(created_at, status)对该查询无效
为什么加了索引,触发器还是慢?
索引存在 ≠ 被用上。触发器场景下,常见干扰项比普通查询更多:函数包装、隐式类型转换、SELECT * 导致回表、统计信息过期等,都会让优化器绕过已有索引。
-
WHERE YEAR(created_at) = 2026—— 即使created_at有索引,函数调用也强制全表扫描 -
WHERE status = NEW.status COLLATE utf8mb4_0900_as_cs—— 显式校对规则可能破坏索引匹配 -
SELECT *查询大宽表,即使WHERE走了索引,后续回表读取 BLOB 字段也会拖慢整体响应 - 用
ANALYZE TABLE log更新统计信息,否则优化器基于过期采样做决策
SELECT,在数据库眼里只是个孤立的、带未知参数的语句。










