EXPLAIN显示type=ALL但有可用索引,是因为优化器基于成本估算选择全表扫描更优;常见原因包括高匹配行数、低区分度索引、大回表开销及过期统计信息。
为什么 EXPLAIN 显示 type=ALL 却有可用索引?
MySQL 优化器不是“看到索引就用”,而是基于成本估算选它认为最省的路径。即使 WHERE 条件字段上有索引,只要优化器预估走索引+回表的总成本 > 全表扫描,就会选 ALL。
常见诱因包括:
-
WHERE条件匹配行数太多(比如status != 'done'在 95% 行都满足时) - 索引区分度低(如
gender只有 'M'/'F' 两值) - 查询返回大量列,导致回表开销远超预期
- 统计信息过期(
INFORMATION_SCHEMA.STATISTICS中的cardinality不准)
怎么查清优化器到底怎么算的成本?
MySQL 8.0+ 支持 optimizer_trace,能直接看到成本计算过程,比猜靠谱得多。
实操步骤:
- 执行
SET optimizer_trace="enabled=on"; - 运行你的慢查询
- 查
SELECT * FROM INFORMATION_SCHEMA.OPTIMIZER_TRACE; - 重点看
steps数组里considered_execution_plans和cost_for_plan
你会发现:哪怕索引扫描的 read_cost 很低,但加上 eval_cost(过滤行数 × 每行评估开销)和 prefix_cost(连接开销)后,总成本反而更高。
FORCE INDEX 强制走索引一定更优吗?
不一定。强制索引只是跳过成本估算,不改变实际执行代价。在某些场景下反而更慢。
疯狂翻译师App
支持屏幕、图片、视频字幕、文档、漫画等多种翻译,准确率高,操作简单。
下载
典型翻车现场:
- 小表(
- 覆盖索引没建好,强制后要回主键二次查找,而全表扫描正好是顺序读
- 索引本身碎片严重(
DATA_FREE大、页利用率低),物理读放大
验证方法:用 BENCHMARK() 或 SYS.STATISTICS 对比两种执行的实际 Handler_read_* 计数,别只看 EXPLAIN 的 rows。
哪些操作真正影响优化器的成本判断?
不是所有“加索引”都能被成本模型正确认知。以下操作直接影响其输入参数:
- 执行
ANALYZE TABLE—— 更新索引基数(cardinality),尤其对大表增删频繁后必须做 - 调整
innodb_stats_persistent_sample_pages(默认 20)—— 样本页越多,基数越准,但ANALYZE更慢 - 避免在
WHERE中对索引列用函数:WHERE YEAR(created_at) = 2023会失效索引,优化器只能退化为全表 - 联合索引顺序必须匹配查询模式:如果经常查
WHERE a=1 AND b>10,(a,b)有效,(b,a)就只能用上a的等值部分
成本模型本身不理解业务语义,它只认统计数字和规则。索引设计、数据分布、配置参数三者稍有偏差,type=ALL 就可能反复出现 —— 这不是 bug,是它在按规则算账。