type=all表示优化器基于成本模型主动放弃索引,认为全表扫描比索引+回表更快;常见原因包括低cardinality、函数操作、非最左前缀匹配、select*或order by引发高回表代价,需通过覆盖索引、统计信息更新和合理建索引修复。

EXPLAIN 显示 type=ALL 就是优化器明确放弃了索引
这不是 bug,也不是配置错误,而是优化器基于成本模型算出来的结果:它认为走索引(查索引页 + 回表取数据)比顺序读聚簇索引更慢。只要 EXPLAIN 中 type 是 ALL,就说明这条语句每次执行都在扫整张表的叶子节点——哪怕 status 上建了索引,哪怕 WHERE 里写了 status = 'paid'。
常见误判信号包括:
-
key字段为空,不代表没索引,只代表这次没被选中 -
rows接近总行数(比如表有 100 万行,rows=923456),说明索引“形同虚设” -
filtered值极低(如filtered=1.00),表示该条件几乎不筛数据
Cardinality 过低会让优化器直接跳过索引
索引列的 Cardinality 值太小(比如总行数 500 万,Cardinality=3),意味着这列只有 3 个不同值(如 is_deleted、gender)。优化器估算发现:查其中一种值要回表几十万次,随机 I/O 开销远超顺序扫描聚簇索引。
这个值不是实时更新的,靠采样估算,容易失真。验证和修复方法:
- 查当前值:
SHOW INDEX FROM orders WHERE Key_name = 'idx_status',看Cardinality列 - 强制刷新:
ANALYZE TABLE orders(注意锁表现象,避开高峰) - 长期可用:
SET GLOBAL innodb_stats_persistent = ON+innodb_stats_auto_recalc = ON - 别单独为低区分度字段建索引;优先把它放在联合索引后位,如
INDEX(user_id, status)
WHERE 条件写法会悄悄破坏索引使用
很多写法看似合理,实则让 B+ 树无法定位物理区间,导致优化器放弃索引:
- 对索引列用函数:
WHERE DATE(create_time) = '2026-06-01'→ 改成create_time >= '2026-06-01' AND create_time - 隐式类型转换:
phone VARCHAR(20)却写WHERE phone = 13800001111→ 改成WHERE phone = '13800001111' - 联合索引跳过最左前缀:
INDEX(a, b, c),却只写WHERE b = 1 AND c = 2 - 范围查询后接等值:
WHERE a = 1 AND b > 10 AND c = 'x'→c列索引失效
SELECT * 和 ORDER BY 会放大回表代价
即使 WHERE 走了索引,SELECT * 或未覆盖的 ORDER BY 也会让优化器觉得“不如直接扫主键”。例如:
- 索引是
INDEX(source_id, source_type),但语句是SELECT * FROM t WHERE source_id = ? ORDER BY id→id不在索引中,必须回表且额外排序 - 优化器发现预估要回表 5 次以上,随机 I/O 成本就超过了顺序扫几页聚簇索引
- 验证方式:
EXPLAIN FORMAT=JSON看query_cost和analyzing_range_alternatives里的成本对比 - 真正有效的解法不是加
FORCE INDEX,而是建覆盖索引(如INDEX(source_id, source_type, id))或限制返回字段
实际执行计划永远比“有没有建索引”更重要。优化器不会撒谎,它只是按当前统计信息和成本模型做了选择——而那个选择,往往暴露的是索引设计与查询模式之间的错配。











