当前位置: 首页 > news >正文

SQL 执行计划解析:从 EXPLAIN 到性能优化的完整指南

🔍 SQL 执行计划解析:从 EXPLAIN 到性能优化的完整指南

文章目录

  • 🔍 SQL 执行计划解析:从 EXPLAIN 到性能优化的完整指南
  • 🎯 一、为什么要理解执行计划
    • 💡 SQL 性能瓶颈的常见原因
    • 📊 性能问题统计
  • ⚡ 二、EXPLAIN 命令详解
    • 🔧 基本用法与输出格式
    • 📝 执行计划解读框架
    • 🔍 三、关键字段深度解析
    • 📋 select_type - 查询类型
    • 🏗️ type - 访问类型(性能关键)
    • 🔑 key - 实际使用的索引
    • 📊 rows - 预估扫描行数
    • 💫 Extra - 额外信息(关键提示)
  • 🛠️ 四、实战案例分析
    • 📈 案例一:单表查询优化
    • 🔗 案例二:多表 JOIN 优化
    • 🎯 案例三:索引覆盖优化
  • 💡 五、总结与优化指南
    • 🎯 执行计划分析 checklist
    • ⚡ 优化黄金法则
    • 🔧 高级优化工具
    • 📚 持续学习资源

🎯 一、为什么要理解执行计划

💡 SQL 性能瓶颈的常见原因

SQL性能瓶颈
全表扫描
索引失效
JOIN顺序不当
临时表使用
排序文件外

执行计划的价值​​:

  • ​​提前发现问题​​:在SQL执行前预测性能

  • ​​​​优化依据​​:提供具体的优化方向

​​- ​​学习工具​​:理解数据库工作原理

📊 性能问题统计

根据对生产环境SQL问题的分析

  • ​​​​65%​​ 的慢查询源于索引使用不当

​​- ​​20%​​ 由于JOIN操作效率低下

​​- ​​10%​​ 因为数据量过大缺乏分页

​​- ​​5%​​ 其他原因(锁竞争、配置问题等)

⚡ 二、EXPLAIN 命令详解

🔧 基本用法与输出格式

​​EXPLAIN 基本命令​​

-- 基本用法
EXPLAIN SELECT * FROM users WHERE age > 25;-- 详细格式(MySQL 8.0+)
EXPLAIN FORMAT=JSON SELECT * FROM users WHERE age > 25;-- 实际执行计划(MySQL 8.0.18+)
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 25;

​​典型EXPLAIN输出​​

+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra       |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+
|  1 | SIMPLE      | users | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 1000 |   100.00 | Using where |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+-------------+

📝 执行计划解读框架

EXPLAIN输出
查询类型分析
访问方法分析
索引使用分析
额外信息分析
select_type
type
key, rows
Extra

🔍 三、关键字段深度解析

🆔 id - 查询标识符
​​id 字段的含义​​:

  • ​​相同id​​:同一查询层级

  • ​​递增id​​:子查询或UNION操作

  • ​​NULL id​​:派生表或临时结果

​​示例分析​​

-- 复杂查询示例
EXPLAIN 
SELECT u.name, (SELECT COUNT(*) FROM orders o WHERE o.user_id = u.id) as order_count
FROM users u
WHERE u.status = 'active';

​​输出解读​​

+----+--------------------+-------+------------+------+---------------+---------+---------+------+------+----------+--------------------------+
| id | select_type        | table | partitions | type | possible_keys | key     | key_len | ref  | rows | filtered | Extra                    |
+----+--------------------+-------+------------+------+---------------+---------+---------+------+------+----------+--------------------------+
|  1 | PRIMARY            | u     | NULL       | ref  | idx_status    | idx_status | 102    | const| 500  |   100.00 | Using where              |
|  2 | DEPENDENT SUBQUERY | o     | NULL       | ref  | idx_user_id   | idx_user_id | 5      | func | 10   |   100.00 | Using index              |
+----+--------------------+-------+------------+------+---------------+---------+---------+------+------+----------+--------------------------+

📋 select_type - 查询类型

​​常见 select_type 值​​

类型描述出现场景
SIMPLE简单查询不含子查询或UNION
PRIMARY最外层查询复杂查询的主查询
SUBQUERY子查询WHERE子句中的子查询
DERIVED派生表FROM子句中的子查询
UNIONUNION查询UNION第二个及以后的查询
UNION RESULTUNION结果UNION结果的临时表

🏗️ type - 访问类型(性能关键)

​​type 性能等级​​(从优到劣):

type性能等级
system
const
eq_ref
ref
range
index
ALL

​​详细解释​​

-- 最佳情况:const(主键或唯一索引等值查询)
EXPLAIN SELECT * FROM users WHERE id = 1;
-- type: const-- 良好情况:ref(非唯一索引等值查询)
EXPLAIN SELECT * FROM users WHERE email = 'user@example.com';
-- type: ref-- 最差情况:ALL(全表扫描)
EXPLAIN SELECT * FROM users WHERE name LIKE '%john%';
-- type: ALL

🔑 key - 实际使用的索引

​​索引使用分析​​:

-- 创建测试索引
CREATE INDEX idx_age ON users(age);
CREATE INDEX idx_city ON users(city);-- 查看索引选择
EXPLAIN SELECT * FROM users WHERE age > 25 AND city = 'Beijing';

​​可能输出​​:

+----+-------------+-------+------------+------+-------------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys     | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------+------------+------+-------------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | users | NULL       | ref  | idx_age,idx_city  | idx_city| 102     | const | 100  |    50.00 | Using where |
+----+-------------+-------+------------+------+-------------------+---------+---------+-------+------+----------+-------------+

📊 rows - 预估扫描行数

​​rows 字段的重要性​​

  • 数值越小越好​​:表示需要处理的数据量少

  • ​​基于统计信息​​:可能不准确,需要定期分析表

  • ​​优化参考​​:帮助识别全表扫描

-- 优化前:全表扫描
EXPLAIN SELECT * FROM users WHERE name LIKE '%john%';
-- rows: 10000(扫描所有行)-- 优化后:索引扫描
EXPLAIN SELECT * FROM users WHERE email = 'john@example.com';
-- rows: 1(只扫描1行)

💫 Extra - 额外信息(关键提示)

​​重要 Extra 值解读​​

Extra 值含义优化建议
Using where使用WHERE过滤正常,无需优化
Using index覆盖索引优秀,无需回表
Using temporary使用临时表考虑优化查询或索引
Using filesort文件排序添加合适的索引
Range checked for each record索引选择困难优化查询条件

🛠️ 四、实战案例分析

📈 案例一:单表查询优化

​​问题查询​​:

SELECT * FROM products 
WHERE category = 'electronics' 
AND price BETWEEN 1000 AND 2000
ORDER BY created_at DESC;

​​初始执行计划​​:

EXPLAIN SELECT * FROM products 
WHERE category = 'electronics' 
AND price BETWEEN 1000 AND 2000
ORDER BY created_at DESC;

​​输出分析​​

+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                       |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+
|  1 | SIMPLE      | products | NULL       | ALL  | NULL          | NULL | NULL    | NULL | 5000 |     5.00 | Using where; Using filesort |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+-----------------------------+

问题诊断​​

  • type: ALL:全表扫描

  • Using filesort:文件排序,性能差

  • 缺少合适索引

​​优化方案​​

-- 创建复合索引
CREATE INDEX idx_category_price_created 
ON products(category, price, created_at);-- 再次查看执行计划
EXPLAIN SELECT * FROM products 
WHERE category = 'electronics' 
AND price BETWEEN 1000 AND 2000
ORDER BY created_at DESC;

​​优化后输出​​

+----+-------------+----------+------------+-------+---------------------------+---------------------------+---------+------+------+----------+-------+
| id | select_type | table    | partitions | type  | possible_keys             | key                       | key_len | ref  | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------------------+---------------------------+---------+------+------+----------+-------+
|  1 | SIMPLE      | products | NULL       | range | idx_category_price_created| idx_category_price_created| 108     | NULL | 50   |   100.00 | Using index condition     |
+----+-------------+----------+------------+-------+---------------------------+---------------------------+---------+------+------+----------+-------+

🔗 案例二:多表 JOIN 优化

​​问题查询​​​​:

SELECT u.name, o.order_date, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.city = 'Beijing'
AND o.status = 'completed'
ORDER BY o.order_date DESC;

​​​​初始执行计划​​​​:

EXPLAIN SELECT u.name, o.order_date, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.city = 'Beijing'
AND o.status = 'completed'
ORDER BY o.order_date DESC;

​​​​输出分析​​​​:

+----+-------------+-------+------------+--------+---------------+---------+---------+-------------------+------+----------+---------------------------------+
| id | select_type | table | partitions | type   | possible_keys | key     | key_len | ref               | rows | filtered | Extra                           |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------------+------+----------+---------------------------------+
|  1 | SIMPLE      | u     | NULL       | ALL    | PRIMARY       | NULL    | NULL    | NULL              | 1000 |    10.00 | Using where; Using filesort     |
|  1 | SIMPLE      | o     | NULL       | eq_ref | PRIMARY       | PRIMARY | 4       | test.u.id         | 1    |    10.00 | Using where                     |
+----+-------------+-------+------------+--------+---------------+---------+---------+-------------------+------+----------+---------------------------------+

问题诊断​​:

  • u表全表扫描

  • 连接顺序可能不是最优

  • 缺少合适的索引

​​优化方案​​

-- 为users表添加城市索引
CREATE INDEX idx_city ON users(city);-- 为orders表添加状态索引
CREATE INDEX idx_status_user ON orders(status, user_id);-- 使用STRAIGHT_JOIN强制连接顺序
EXPLAIN SELECT STRAIGHT_JOIN u.name, o.order_date, o.amount
FROM users u
JOIN orders o ON u.id = o.user_id
WHERE u.city = 'Beijing'
AND o.status = 'completed'
ORDER BY o.order_date DESC;

​​优化后输出​​:

+----+-------------+-------+------------+------+---------------------+---------------------+---------+--------------------+------+----------+---------------------------------+
| id | select_type | table | partitions | type | possible_keys       | key                 | key_len | ref                | rows | filtered | Extra                           |
+----+-------------+-------+------------+------+---------------------+---------------------+---------+--------------------+------+----------+---------------------------------+
|  1 | SIMPLE      | u     | NULL       | ref  | PRIMARY,idx_city    | idx_city            | 102     | const              | 100  |   100.00 | Using temporary; Using filesort |
|  1 | SIMPLE      | o     | NULL       | ref  | idx_status_user     | idx_status_user     | 104     | test.u.id,const    | 5    |   100.00 | NULL                            |
+----+-------------+-------+------------+------+---------------------+---------------------+---------+--------------------+------+----------+---------------------------------+

🎯 案例三:索引覆盖优化

​​问题查询​​​​:

SELECT COUNT(*) FROM user_actions 
WHERE user_id = 1001 AND action_date > '2023-01-01';

​​​​初始情况​​​​:

-- 现有索引:INDEX (user_id)
EXPLAIN SELECT COUNT(*) FROM user_actions 
WHERE user_id = 1001 AND action_date > '2023-01-01';

​​​​输出分析​​​​:

+----+-------------+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
| id | select_type | table       | partitions | type | possible_keys | key     | key_len | ref   | rows | filtered | Extra       |
+----+-------------+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+
|  1 | SIMPLE      | user_actions| NULL       | ref  | idx_user_id   | idx_user_id | 5      | const | 500  |    33.33 | Using where |
+----+-------------+-------------+------------+------+---------------+---------+---------+-------+------+----------+-------------+

优化方案​​:

-- 创建覆盖索引
CREATE INDEX idx_user_id_action_date 
ON user_actions(user_id, action_date);-- 再次执行
EXPLAIN SELECT COUNT(*) FROM user_actions 
WHERE user_id = 1001 AND action_date > '2023-01-01';

​​优化后输出​​:

+----+-------------+-------------+------------+-------+---------------------------+---------------------------+---------+------+------+----------+--------------------------+
| id | select_type | table       | partitions | type  | possible_keys             | key                       | key_len | ref  | rows | filtered | Extra                    |
+----+-------------+-------------+------------+-------+---------------------------+---------------------------+---------+------+------+----------+--------------------------+
|  1 | SIMPLE      | user_actions| NULL       | range | idx_user_id_action_date   | idx_user_id_action_date   | 8       | NULL | 100  |   100.00 | Using where; Using index |
+----+-------------+-------------+------------+-------+---------------------------+---------------------------+---------+------+------+----------+--------------------------+

优化效果​​

  • Using index:实现索引覆盖,无需回表

  • 查询性能提升 3-5 倍

💡 五、总结与优化指南

🎯 执行计划分析 checklist

​​快速诊断流程​​:

查看执行计划
type = ALL?
全表扫描警告
Extra有filesort?
添加合适索引
优化排序字段索引
rows值过大?
优化查询条件
查询已优化

⚡ 优化黄金法则

​​索引优化策略​​

  • 最左前缀原则​​:复合索引的顺序很重要

  • ​​覆盖索引​​:尽可能让索引包含查询字段

  • ​​索引选择性​​:选择高区分度的列建索引

​​查询优化技巧​​:

-- 避免 SELECT *
SELECT id, name, email FROM users; -- 只选择需要的字段-- 优化 WHERE 条件
SELECT * FROM products WHERE price > 1000; -- 使用范围查询
SELECT * FROM products WHERE id IN (1,2,3); -- 使用IN而不是OR-- 合理使用 LIMIT
SELECT * FROM large_table LIMIT 1000; -- 限制返回行数

🔧 高级优化工具

​​MySQL 8.0 新特性​​

-- 执行计划可视化
EXPLAIN FORMAT=TREE SELECT * FROM users WHERE age > 25;-- 实际执行分析
EXPLAIN ANALYZE SELECT * FROM users WHERE age > 25;-- 索引建议
SELECT * FROM sys.schema_index_statistics;

​​性能监控脚本​​:

-- 查找全表扫描的查询
SELECT * FROM sys.statements_with_full_table_scans;-- 查找需要索引的查询
SELECT * FROM sys.statements_with_errors_or_warnings;

📚 持续学习资源

​​推荐分析命令​​

-- 定期分析表统计信息
ANALYZE TABLE users;-- 检查索引使用情况
SHOW INDEX FROM users;-- 监控慢查询
SHOW VARIABLES LIKE 'slow_query_log%';
http://www.dtcms.com/a/406789.html

相关文章:

  • jupyter notebook继续学习
  • 力扣2381. 字母移位 II
  • 平和县建设局网站安徽经工建设集团网站
  • Vue 配置代理
  • CatCTF2022 web wp
  • 知乎免费阅读网站石家庄新闻综合频道节目回看
  • 做网站后台主要负责什么最新室内装修风格图片
  • 机器人行业灵巧手专题研究报告
  • hive SQL查询与函数
  • 网站维护 关站 seo百度首页广告
  • 搜索引擎的网站有哪些网页设计与网站建设 倪宝童
  • Prompt Optimizer 提示词优化器安装使用
  • 淘宝网站建设的优点app推广是什么工作
  • 【C++】23. C++11(上)
  • 第三方软件登记测评机构:【LoadRunner脚本录制与调试】
  • 摄影网站开发的背景西安网站 技术支持牛商网
  • A股大盘数据-20250925分析
  • 旋转设备状态监测传感器选型要点
  • echarts项目积累
  • VS2022调试技巧
  • Vue 3 组合式 API 生命周期钩子学习笔记
  • shardingsphere加载过程
  • MinerU介绍安装
  • 好的建设网站关于建立企业网站的方案内容
  • 在mac上面使用parquet-cli查看parquet文件
  • 织梦资讯门户网站模板公司成立后网站建设
  • linux入门4.4(DHCP和DNS服务器)
  • 存储卷备份策略在海外vps数据安全中的基础规范
  • 基于MATLAB的热晕相位屏仿真
  • 天津网站优化怎么样成都金牛区建设局网站