explain显示type=all并非索引未建好,而是优化器基于成本模型判断走索引(预估扫描行数×回表开销)比全表扫描更慢;常见原因包括低基数字段、select*、联合索引不满足最左前缀、统计信息过期等。

为什么 EXPLAIN 显示 type=ALL,明明字段有索引?
不是索引没建成功,而是优化器算了一笔账:走索引反而更慢。MySQL 5.6+ 的优化器基于成本模型(cost-based optimizer)决定是否用索引,核心判断依据是「预估扫描行数 × 回表开销」是否小于全表扫描成本。
常见触发点包括:
-
WHERE条件列基数太低(比如status只有 0/1,Cardinality接近总行数的 1/N) - 查询返回大量字段(
SELECT *),导致回表次数多、I/O 翻倍 - 联合索引未满足最左前缀,但优化器误判为“可用”,实际执行时发现过滤率差,主动弃用
- 统计信息过期(
innodb_stats_persistent=ON时,数据变更超 1/20 就该更新,但没触发)
SHOW INDEX 和 ANALYZE TABLE 要一起看
SHOW INDEX FROM t 只告诉你索引存在,Cardinality 字段显示的是采样估算值,不一定准。当表数据批量导入或长期未更新后,这个值可能严重偏离真实分布。
实操建议:
- 先查当前基数:
SHOW INDEX FROM t WHERE Key_name = 'idx_status';,关注Cardinality是否明显偏低(比如总行数 100 万,Cardinality只有 2) - 强制刷新统计:
ANALYZE TABLE t;(注意:该操作会锁表,生产环境避开高峰) - 若频繁变更,可设
innodb_stats_persistent=ON+innodb_stats_auto_recalc=ON,让 MySQL 自动维护
联合索引下 ORDER BY 导致索引“看似失效”
例如建了 INDEX idx_a_b_c (a,b,c),但执行 SELECT * FROM t WHERE a = 1 ORDER BY b, c 却没走索引排序(Extra 出现 Using filesort),甚至 type 变成 ALL。
原因不是索引建错了,而是优化器发现:
- 满足
a = 1的行太多(比如占全表 40%),用索引定位后再回表取所有字段,成本高于直接扫聚簇索引 - 如果加了
LIMIT 10,它又会切回去用索引——因为只取前 10 行,回表开销骤降 - 如果只查索引覆盖字段(如
SELECT a,b,c),Extra会变成Using index,这时索引才真正“全效”
真正该盯住的三个 EXPLAIN 字段
别只看 key 是否为空,重点比对这三个值的实际含义:
-
rows:优化器预估要检查的行数。如果远大于实际匹配数(比如WHERE x = 1本应只返回 5 行,却显示rows=80000),说明统计不准或选择率误判 -
filtered:表示条件过滤后剩余行数的百分比(0–100)。若filtered=10.00,意味着只保留约 1/10,说明该条件选择性尚可;若只有1.00,基本等于无效过滤 -
Extra中的Using index condition(ICP)出现,说明 MySQL 在引擎层就做了条件过滤,这是好信号;若出现Using temporary或多次Using filesort,基本可以断定查询结构需要重写
最易被忽略的一点:优化器的成本模型依赖于磁盘 I/O、内存带宽、CPU 计算等参数,而这些在云数据库或容器化部署中常被抽象掉,导致本地测试结果和线上表现不一致。上线前务必在同构环境跑 EXPLAIN FORMAT=TRADITIONAL + SELECT ... FOR UPDATE 类似压力下的实际执行计划。











