知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!
MySQL 执行计划深度分析指南
一、执行计划核心字段解析
通过 EXPLAIN
输出的每个字段都暗藏优化线索:
字段 | 核心意义 | 优化方向 |
---|---|---|
id | 查询序列号(相同id为同一层级,不同id按序执行) | 复杂嵌套查询时,观察子查询执行顺序是否合理 |
select_type | 查询类型(SIMPLE/PRIMARY/SUBQUERY/DERIVED/UNION等) | DERIVED(派生表)考虑是否可用物化视图替代;UNION RESULT关注临时表性能 |
partitions | 匹配的分区(仅分区表有效) | 验证分区裁剪是否生效,避免扫描多余分区 |
type | 访问类型(性能排序:system > const > eq_ref > ref > range > index > ALL ) |
目标是将type 提升至range 及以上,避免ALL 全表扫描 |
possible_keys | 可能使用的索引 | 若列出的索引未被使用,需检查索引失效原因 |
key | 实际使用的索引 | 强制使用索引:FORCE INDEX ;忽略索引:IGNORE INDEX |
key_len | 索引使用的字节数 | 计算复合索引实际使用长度,判断是否达到最左前缀要求 |
ref | 索引匹配的列或常量 | const 表示常量引用;列名表示关联字段 |
rows | 预估扫描行数(InnoDB统计信息) | 对比rows 与真实行数的偏差,过大时需ANALYZE TABLE 更新统计信息 |
filtered | 存储引擎层过滤后的数据占比(0~100) | 低过滤率(如<10%)考虑添加更精准的WHERE条件或覆盖索引 |
Extra | 附加信息(Using filesort /Using temporary 等) |
关键优化信号源,需针对性处理 |
二、Type 访问类型深度剖析
-
const/system
- 触发条件 :通过主键或唯一索引定位单行(
WHERE id = 1
) - 优化价值:性能极致,无需优化
- 触发条件 :通过主键或唯一索引定位单行(
-
eq_ref
-
场景:JOIN查询中,关联字段为主键或唯一索引
-
示例 :
sqlEXPLAIN SELECT * FROM orders JOIN users ON orders.user_id = users.id; -- users.id是主键
-
-
ref
- 场景:非唯一索引等值匹配
- 优化点 :
- 联合索引需满足最左前缀
- 区分度低的索引(如性别)可能直接全表扫描
-
range
-
触发操作 :
BETWEEN
、IN
、>
等范围查询 -
索引设计 :
sql-- 索引 (age, city) WHERE age > 25 AND city = 'Beijing' -- 只能用到age列 -- 优化:调整索引顺序为(city, age)
-
-
index
- 全索引扫描 :
SELECT
字段全在索引中,但需遍历整个索引树 - 对比ALL的优势:索引大小通常小于表数据
- 全索引扫描 :
三、Extra 字段关键值处理方案
Extra值 | 产生原因 | 优化策略 |
---|---|---|
Using filesort | 排序字段无索引或不符合索引顺序 | 1. 创建排序字段索引 2. 利用索引天然有序性(如时间字段) |
Using temporary | 使用临时表(GROUP BY/ORDER BY/DISTINCT) | 1. 增加覆盖索引 2. 调大tmp_table_size 3. 避免非必要DISTINCT |
Using index | 覆盖索引(无需回表) | 确认查询字段是否全部包含在索引中 |
Using where | Server层对存储引擎返回的数据进行过滤 | 检查WHERE条件是否可以利用索引下推(ICP) |
Select tables optimized away | 查询可被优化为直接读取结果(如MIN/MAX使用索引) | 无需优化 |
Using join buffer | 关联字段无可用索引,使用Block Nested-Loop | 1. 为关联字段添加索引 2. 调大join_buffer_size (治标不治本) |
四、复杂场景执行计划分析
1. JOIN 查询优化
-
驱动表选择原则 :
- 小表驱动大表
- 带WHERE条件的表优先作为驱动表
-
Index Nested-Loop Join :
sql-- 确保被驱动表的关联字段有索引 EXPLAIN SELECT * FROM small_table s JOIN large_table l ON s.id = l.s_id; -- l.s_id需有索引
2. 子查询陷阱
-
DEPENDENT SUBQUERY (相关子查询):
sqlEXPLAIN SELECT * FROM users u WHERE EXISTS ( SELECT 1 FROM orders o WHERE o.user_id = u.id -- 外层依赖 );
优化 :改写为JOIN
sqlSELECT u.* FROM users u JOIN orders o ON u.id = o.user_id;
3. 索引合并(Index Merge)
-
场景 :多个单列索引的OR组合
sql-- 索引 (a), (b) EXPLAIN SELECT * FROM t WHERE a = 1 OR b = 2; -- 出现Using union(a,b)
风险 :合并操作消耗CPU,优先使用复合索引
sqlALTER TABLE t ADD INDEX idx_a_b (a, b);
五、高阶技巧
-
JSON格式执行计划
sqlEXPLAIN FORMAT=JSON SELECT ... -- 获取成本估算、触发条件等详细信息
-
索引选择性计算
sqlSELECT COUNT(DISTINCT column)/COUNT(*) AS selectivity FROM table; -- 选择性>0.2适合建索引
-
执行计划可视化工具
- MySQL Workbench Visual Explain
- Percona Toolkit 的
pt-visual-explain
六、实战调优流程
-
抓取目标SQL
- 慢查询日志
performance_schema.events_statements_summary_by_digest
-
EXPLAIN解析
- 重点关注
type
、key_len
、rows
、Extra
- 重点关注
-
索引优化
- 添加缺失索引
- 调整索引顺序
- 使用覆盖索引
-
重写SQL
- 拆分复杂查询
- 消除临时表/文件排序
- 利用JOIN代替子查询
-
验证效果
- 对比优化前后
EXPLAIN
结果 - 监控QPS和Latency变化
- 对比优化前后