mysql-EXPLAIN执行计划分析

87 阅读4分钟

知其然要知其所以然,探索每一个知识点背后的意义,你知道的越多,你不知道的越多,一起学习,一起进步,如果文章感觉对您有用的话,关注、收藏、点赞,有困惑的地方请评论,我们一起交流!

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 访问类型深度剖析

  1. const/system

    • 触发条件:通过主键或唯一索引定位单行(WHERE id = 1
    • 优化价值:性能极致,无需优化
  2. eq_ref

    • 场景:JOIN查询中,关联字段为主键或唯一索引
    • 示例
      EXPLAIN SELECT * FROM orders 
      JOIN users ON orders.user_id = users.id; -- users.id是主键
      
  3. ref

    • 场景:非唯一索引等值匹配
    • 优化点
      • 联合索引需满足最左前缀
      • 区分度低的索引(如性别)可能直接全表扫描
  4. range

    • 触发操作BETWEENIN> 等范围查询
    • 索引设计
      -- 索引 (age, city)
      WHERE age > 25 AND city = 'Beijing' -- 只能用到age列
      -- 优化:调整索引顺序为(city, age)
      
  5. 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 whereServer层对存储引擎返回的数据进行过滤检查WHERE条件是否可以利用索引下推(ICP)
Select tables optimized away查询可被优化为直接读取结果(如MIN/MAX使用索引)无需优化
Using join buffer关联字段无可用索引,使用Block Nested-Loop1. 为关联字段添加索引
2. 调大join_buffer_size(治标不治本)

四、复杂场景执行计划分析

1. JOIN 查询优化
  • 驱动表选择原则
    • 小表驱动大表
    • 带WHERE条件的表优先作为驱动表
  • Index Nested-Loop Join
    -- 确保被驱动表的关联字段有索引
    EXPLAIN SELECT * FROM small_table s 
    JOIN large_table l ON s.id = l.s_id; -- l.s_id需有索引
    
2. 子查询陷阱
  • DEPENDENT SUBQUERY(相关子查询):
    EXPLAIN SELECT * FROM users u 
    WHERE EXISTS (
      SELECT 1 FROM orders o 
      WHERE o.user_id = u.id -- 外层依赖
    );
    
    优化:改写为JOIN
    SELECT u.* FROM users u 
    JOIN orders o ON u.id = o.user_id;
    
3. 索引合并(Index Merge)
  • 场景:多个单列索引的OR组合
    -- 索引 (a), (b)
    EXPLAIN SELECT * FROM t WHERE a = 1 OR b = 2;
    -- 出现Using union(a,b)
    
    风险:合并操作消耗CPU,优先使用复合索引
    ALTER TABLE t ADD INDEX idx_a_b (a, b);
    

五、高阶技巧

  1. JSON格式执行计划

    EXPLAIN FORMAT=JSON SELECT ...
    -- 获取成本估算、触发条件等详细信息
    
  2. 索引选择性计算

    SELECT 
      COUNT(DISTINCT column)/COUNT(*) AS selectivity 
    FROM table;
    -- 选择性>0.2适合建索引
    
  3. 执行计划可视化工具

    • MySQL Workbench Visual Explain
    • Percona Toolkit 的 pt-visual-explain

六、实战调优流程

  1. 抓取目标SQL

    • 慢查询日志
    • performance_schema.events_statements_summary_by_digest
  2. EXPLAIN解析

    • 重点关注typekey_lenrowsExtra
  3. 索引优化

    • 添加缺失索引
    • 调整索引顺序
    • 使用覆盖索引
  4. 重写SQL

    • 拆分复杂查询
    • 消除临时表/文件排序
    • 利用JOIN代替子查询
  5. 验证效果

    • 对比优化前后EXPLAIN结果
    • 监控QPS和Latency变化