MySQL 选择最优执行计划,不是靠“猜”,而是基于成本模型(Cost-Based Optimizer, CBO)对多个候选路径做量化评估后选成本最低的那个——
cost越低,越可能被选中。
看懂 EXPLAIN 是判断“是否最优”的唯一入口
不查
EXPLAIN,就等于闭眼调优。它暴露了优化器的全部决策依据:
type字段直接反映访问效率:从
const→
eq_ref→
ref→
range→
index→
ALL,性能逐级下降;出现
ALL基本意味着没走索引或索引失效
key和
possible_keys不一致?说明优化器放弃了你建的索引——常见原因包括:索引列参与了函数/表达式、隐式类型转换、
WHERE条件未覆盖最左前缀
rows是预估扫描行数,不是实际值;但若远大于结果集(比如查 1 行却扫 10 万行),大概率存在索引未命中或统计信息过期
Extra中的
Using filesort或
Using temporary是强信号:ORDER BY / GROUP BY 没走索引,正在用磁盘临时文件排序或建临时表
为什么明明有索引,优化器却不选?
索引存在 ≠ 索引可用。优化器弃用索引的核心原因是:它算出来用这个索引的成本反而更高。
数据分布倾斜:比如某字段 95% 值为'active',即使加了索引,优化器也会认为全表扫描更便宜(因为要回表读取大量数据) 统计信息陈旧:
ANALYZE TABLE没跑过,优化器基于错误基数估算成本;尤其在大批量 INSERT/DELETE 后必须手动刷新 联合索引顺序错位:
KEY idx_name_age (name, age)支持
WHERE name = ?或
WHERE name = ? AND age > ?,但不支持
WHERE age = ?隐式转换强制放弃索引:
WHERE phone = 13800138000(phone 是
VARCHAR)会触发字符串→数字转换,导致索引失效
EXPLAIN ANALYZE 才是真·照妖镜
EXPLAIN只显示预估,而
EXPLAIN ANALYZE(MySQL 8.0+)会真实执行并返回各阶段耗时、实际扫描行数、是否真正用了索引等硬指标。
EXPLAIN ANALYZE SELECT * FROM employees WHERE name = '张三' AND age > 25;对比
rows(预估)和
actual rows(实际):若相差 10 倍以上,说明统计不准或查询条件选择性误判 观察
actual time分布:如果 “Index lookup” 很快但 “Read data” 占比极高,说明回表开销大,考虑改成覆盖索引 注意 “Materialize” 或 “Create temporary table” 出现场景:这类操作无法通过加索引消除,需重构 SQL 或调整
tmp_table_size等参数
真正卡住性能的,往往不是“有没有索引”,而是“优化器信不信这个索引值得用”。别只盯着
CREATE INDEX,多跑
EXPLAIN ANALYZE,再结合
SHOW INDEX和
INFORMATION_SCHEMA.STATISTICS看基数,才能看清成本模型背后的真实逻辑。
