mysql优化器基于成本模型选择执行计划,依赖表行数、索引基数、直方图等统计信息;统计过期或隐式转换会导致误判;explain中type、key、rows、extra四列最关键;force index可能因回表开销更大而变慢;需同步调整sql、索引与统计信息。

MySQL优化器如何选择执行计划
MySQL不按SQL字面顺序执行,而是由优化器生成成本最低的执行计划。这个过程不是确定性的——同一SQL在不同数据分布、索引状态或统计信息下,可能生成完全不同的执行计划。
优化器基于cost_model估算每个候选计划的I/O和CPU开销,核心输入包括:表行数(INFORMATION_SCHEMA.TABLES.TABLE_ROWS)、索引基数(SHOW INDEX FROM tbl)、列值分布直方图(MySQL 8.0+)、以及是否启用optimizer_switch中的各项规则(如index_merge=on)。
- 统计信息过期会导致误判:执行
ANALYZE TABLE t强制刷新,而非依赖自动采样 -
FORCE INDEX能绕过优化器选择,但仅解决表层问题;真正要查的是为什么优化器认为全表扫描更“便宜” - 隐式类型转换(如
WHERE varchar_col = 123)会让索引失效,且优化器仍可能选错路径,因为类型转换发生在执行阶段,优化器无法准确预估过滤效果
EXPLAIN输出里哪些字段最关键
EXPLAIN不是执行日志,而是优化器“打算怎么做”的快照。重点关注type、key、rows、Extra四列,它们直接暴露策略缺陷:
-
type为ALL或index:大概率缺失有效索引,或查询条件未覆盖索引最左前缀 -
key为NULL:优化器明确放弃使用索引,常见于OR条件未被索引覆盖、函数包裹字段(如WHERE YEAR(create_time) = 2023) -
rows远大于实际结果集:说明索引选择不当或统计不准,rows是优化器预估的扫描行数,不是返回行数 -
Extra含Using filesort或Using temporary:排序或分组未走索引,需检查ORDER BY/GROUP BY字段是否在索引中连续出现
为什么FORCE INDEX有时反而更慢
强制指定索引会跳过成本估算,但不改变底层执行逻辑。如果强制的索引无法覆盖查询所需字段(即非覆盖索引),MySQL仍需回表读取完整行,而优化器原本选的全表扫描可能因顺序I/O更快。
- 复合索引
(a,b,c)上执行SELECT * FROM t WHERE a=1 ORDER BY b,即使FORCE INDEX(a,b),c字段仍需回表;若表不大,全表扫描+内存排序反而更优 -
FORCE INDEX不阻止Using filesort:它只影响访问路径,不影响排序方式 - 用
EXPLAIN FORMAT=JSON查看used_key_parts和used_columns,确认索引是否真正被“用满”
统计信息不准时怎么快速验证
优化器严重依赖统计信息,但ANALYZE TABLE默认采样率低(innodb_stats_persistent_sample_pages=20),大表容易误判。
- 临时提高采样精度:
SET SESSION innodb_stats_persistent_sample_pages = 100,再执行ANALYZE TABLE t - 检查实际基数是否失真:
SELECT COUNT(DISTINCT col) FROM tvsSHOW INDEX FROM t里的Cardinality值,偏差超5倍就该怀疑 - 禁用持久化统计(
innodb_stats_persistent=OFF)可让每次重启后重新采样,适合测试环境快速验证,但生产慎用
执行计划不是配置出来的,是数据、索引、统计信息和查询结构共同作用的结果。改SQL、加索引、刷统计,三者常需同步调整,单点改动往往无效。










