MySQL优化器为何选择ALL类型而非index类型?
为什么你的MySQL执行计划没走索引?
嘿,这个问题我在日常调优里碰到过好多次,咱们来一步步捋清楚可能的原因:
1. 数据选择性太差(最常见的原因)
MySQL优化器的核心目标是最小化IO开销。如果你的查询条件from_date>='1985-01-01'匹配了表中绝大多数数据(比如超过30%-40%的行),优化器会觉得:走索引需要先查索引页,再根据索引里的主键回表去取所有字段(因为你用了SELECT *),这个过程的IO开销反而比直接全表扫描更高。
你可以跑这两个SQL验证一下:
-- 统计符合条件的行数 SELECT COUNT(*) FROM dept_emp WHERE from_date>='1985-01-01'; -- 统计表总行数 SELECT COUNT(*) FROM dept_emp;
如果前者占后者的比例很高(比如超过30%),那优化器放弃索引是合理的选择。
2. 索引统计信息过时
MySQL优化器依赖表和索引的统计信息来估算行数和成本。如果你的表最近有大量数据插入、更新或者删除,统计信息可能已经不准确了,导致优化器做出错误的判断。
解决方法是手动更新统计信息:
ANALYZE TABLE dept_emp;
更新完之后再跑EXPLAIN看看执行计划有没有变化。
3. 字段类型或索引类型不匹配(可能性较低但值得排查)
- 确认
from_date字段的类型是不是DATE/DATETIME?如果是字符串类型,而你的查询条件用了日期格式,可能会导致隐式类型转换,让索引失效。 - 确认你创建的是普通B-tree索引吗?如果是其他类型(比如全文索引),那肯定没法支持范围查询。
4. 优化器成本模型的判断
MySQL的成本模型会计算不同执行计划的成本:
- 全表扫描成本 = 表的数据页数量 × 单页读取成本
- 索引扫描成本 = 索引页数量 × 单页读取成本 + 回表行数 × 回表成本
如果回表的行数太多,索引扫描的总成本就会超过全表扫描,优化器就会选择type=ALL的全表扫描。
验证方法
如果你想确认是不是成本问题,可以强制走索引试试:
EXPLAIN SELECT * FROM dept_emp FORCE INDEX(ix_fromdate) WHERE from_date>='1985-01-01';
然后对比强制索引和全表扫描的实际执行时间,如果全表扫描更快,那优化器的选择确实是对的;如果强制索引更快,那可能是统计信息的问题,需要重新分析表。
内容的提问来源于stack exchange,提问作者JoonT
相关产品推荐
相关产品推荐

