EXPLAIN详解:MySQL查询优化必备工具

本文涉及的产品
RDS MySQL Serverless 基础系列,0.5-2RCU 50GB
RDS MySQL DuckDB 分析主实例,集群系列 8核16GB
RDS MySQL Serverless 高可用系列,价值2615元额度,1个月
简介: 本文深入解析了MySQL中的EXPLAIN命令,帮助开发者识别和优化慢查询。通过分析执行计划,读者可以了解查询的性能瓶颈,并采取相应措施提升数据库效率。

💡 摘要:你是否曾经面对慢查询束手无策?是否想知道MySQL如何执行你的SQL语句?是否希望提前发现性能瓶颈?

EXPLAIN就是你的答案!这个看似简单的命令,却是MySQL查询优化的终极武器。它能揭示查询执行的每一个细节,让你像拥有X光透视眼一样看透数据库的执行计划。

本文将带你深度解析EXPLAIN的每一个输出字段,通过真实案例教你如何识别性能问题、优化查询策略,让你的数据库查询速度提升数倍!


一、EXPLAIN基础:为什么它是优化必备工具?

1. EXPLAIN的核心价值

应用场景 解决的问题 优化效果
慢查询分析 找出性能瓶颈 查询时间从秒级到毫秒级
索引优化 验证索引使用情况 减少90%的磁盘I/O
联表优化 优化JOIN顺序和方式 提升复杂查询性能
排序优化 消除filesort操作 排序操作加速10倍

2. EXPLAIN的基本用法

sql

-- 基本语法

EXPLAIN SELECT * FROM users WHERE email = 'user@example.com';


-- 详细格式(MySQL 8.0+)

EXPLAIN FORMAT=JSON SELECT * FROM orders WHERE user_id = 1001;


-- 分析连接查询

EXPLAIN

SELECT u.username, o.order_date, o.amount

FROM users u

JOIN orders o ON u.id = o.user_id

WHERE u.city = '北京' AND o.amount > 1000;


-- 与实际执行计划对比(MySQL 8.0+)

EXPLAIN ANALYZE SELECT * FROM products WHERE price > 100;


二、深度解析EXPLAIN输出字段

1. 核心字段详解表

字段 含义 优化意义 理想值
id 查询标识符 识别复杂查询中的执行顺序 数字越小优先级越高
select_type 查询类型 了解查询的复杂程度 SIMPLE, PRIMARY
table 访问的表 知道正在操作哪个表 表名或别名
partitions 匹配的分区 分区表优化 NULL(未分区)
type 访问类型 最重要的性能指标 const, eq_ref, ref, range
possible_keys 可能使用的索引 索引选择分析 实际使用的索引
key 实际使用的索引 索引使用验证 索引名称
key_len 索引长度 索引使用效率 越短越好
ref 索引匹配 连接条件分析 const, func, NULL
rows 预估扫描行数 性能关键指标 越小越好
filtered 过滤比例 条件过滤效率 100%为最佳
Extra 额外信息 优化重点区域 Using index, Using where

2. type访问类型深度解析

sql

-- 性能从优到劣排序:

-- 1. system: 系统表,只有一行数据

EXPLAIN SELECT * FROM mysql.user WHERE host = 'localhost' AND user = 'root';


-- 2. const: 主键或唯一索引等值查询

EXPLAIN SELECT * FROM users WHERE id = 1;


-- 3. eq_ref: 唯一索引关联查询

EXPLAIN SELECT * FROM orders o

JOIN users u ON o.user_id = u.id

WHERE o.order_id = 1001;


-- 4. ref: 非唯一索引等值查询

EXPLAIN SELECT * FROM users WHERE email = 'test@example.com';


-- 5. range: 索引范围查询

EXPLAIN SELECT * FROM orders WHERE user_id BETWEEN 1000 AND 2000;


-- 6. index: 全索引扫描

EXPLAIN SELECT COUNT(*) FROM users;


-- 7. ALL: 全表扫描(需要优化)

EXPLAIN SELECT * FROM users WHERE name LIKE '%john%';

3. Extra字段常见值解析

sql

-- 正面信息(良好):

-- • Using index: 使用覆盖索引

EXPLAIN SELECT id, email FROM users WHERE email = 'test@example.com';


-- • Using index condition: 索引下推优化

EXPLAIN SELECT * FROM users WHERE email LIKE 'test%' AND age > 25;


-- 负面信息(需要优化):

-- • Using filesort: 需要额外排序

EXPLAIN SELECT * FROM users ORDER BY created_at DESC;


-- • Using temporary: 使用临时表

EXPLAIN SELECT DISTINCT department FROM employees;


-- • Using where: 在存储引擎层后过滤

EXPLAIN SELECT * FROM users WHERE name LIKE '%john%';


三、实战案例:EXPLAIN优化慢查询

1. 案例一:缺失索引导致的全表扫描

sql

-- 原始查询(执行时间:2.1秒)

EXPLAIN SELECT * FROM orders WHERE user_id = 1001 AND status = 'completed';


-- EXPLAIN输出:

/*

id: 1

select_type: SIMPLE

table: orders

type: ALL

key: NULL

key_len: NULL

rows: 1000000

Extra: Using where

*/


-- 问题分析:全表扫描100万行数据

-- 解决方案:添加复合索引

CREATE INDEX idx_user_status ON orders(user_id, status);


-- 优化后EXPLAIN:

/*

id: 1

select_type: SIMPLE

table: orders

type: ref

key: idx_user_status

key_len: 8

rows: 15

Extra: Using index condition

*/


-- 优化效果:查询时间从2.1秒 → 0.002秒

2. 案例二:低效的排序操作

sql

-- 原始查询(执行时间:1.8秒)

EXPLAIN SELECT * FROM products

WHERE category_id = 101

ORDER BY price DESC

LIMIT 20;


-- EXPLAIN输出:

/*

id: 1

select_type: SIMPLE

table: products

type: ref

key: idx_category

key_len: 4

rows: 5000

Extra: Using filesort

*/


-- 问题分析:虽然使用了索引,但需要filesort

-- 解决方案:创建支持排序的复合索引

CREATE INDEX idx_category_price ON products(category_id, price DESC);


-- 优化后EXPLAIN:

/*

id: 1

select_type: SIMPLE

table: products

type: ref

key: idx_category_price

key_len: 4

rows: 5000

Extra: Using index

*/


-- 优化效果:查询时间从1.8秒 → 0.015秒,消除filesort

3. 案例三:复杂的联表查询优化

sql

-- 原始查询(执行时间:3.5秒)

EXPLAIN

SELECT u.username, o.order_date, p.product_name, oi.quantity

FROM users u

JOIN orders o ON u.id = o.user_id

JOIN order_items oi ON o.order_id = oi.order_id

JOIN products p ON oi.product_id = p.product_id

WHERE u.city = '上海' AND o.order_date > '2023-01-01';


-- EXPLAIN输出显示:

-- • users表: type=ALL (全表扫描)

-- • orders表: type=ALL

-- • 需要优化连接顺序和索引


-- 解决方案:添加缺失索引并优化连接顺序

CREATE INDEX idx_user_city ON users(city);

CREATE INDEX idx_order_user_date ON orders(user_id, order_date);

CREATE INDEX idx_order_item_order ON order_items(order_id);

CREATE INDEX idx_product_id ON products(product_id);


-- 优化后EXPLAIN显示所有表都使用了索引,查询时间:3.5秒 → 0.2秒


四、EXPLAIN FORMAT=JSON深度解析

1. JSON格式的详细输出

sql

EXPLAIN FORMAT=JSON

SELECT u.username, o.amount

FROM users u

JOIN orders o ON u.id = o.user_id

WHERE u.email LIKE 'john%' AND o.amount > 1000;


-- JSON输出包含的关键信息:

{

 "query_block": {

   "select_id": 1,

   "cost_info": {

     "query_cost": "15.75"  -- 查询总成本

   },

   "nested_loop": [

     {

       "table": {

         "table_name": "u",

         "access_type": "range",  -- 访问类型

         "possible_keys": ["idx_email"],

         "key": "idx_email",

         "used_key_parts": ["email"],

         "rows_examined_per_scan": 50,

         "rows_produced_per_join": 50,

         "filtered": "100.00",

         "cost_info": {

           "read_cost": "5.25",

           "eval_cost": "5.00",

           "prefix_cost": "10.25"

         }

       }

     },

     {

       "table": {

         "table_name": "o",

         "access_type": "ref",

         "key": "idx_user_amount",

         "used_key_parts": ["user_id", "amount"],

         "rows_examined_per_scan": 10,

         "rows_produced_per_join": 5,

         "filtered": "50.00",

         "cost_info": {

           "read_cost": "5.00",

           "eval_cost": "0.50",

           "prefix_cost": "15.75"

         }

       }

     }

   ]

 }

}

2. 成本分析实战

sql

-- 通过成本分析选择最优索引

EXPLAIN FORMAT=JSON

SELECT * FROM orders

WHERE user_id = 1001 OR status = 'completed';


-- 分析不同索引的成本差异:

-- 方案1: 使用索引合并 (cost: 25.30)

-- 方案2: 全表扫描 (cost: 18.50) ← 优化器选择这个

-- 方案3: 强制使用索引 (cost: 30.75)


-- 优化建议:创建更好的复合索引

CREATE INDEX idx_user_status ON orders(user_id, status);


五、EXPLAIN ANALYZE:实际执行计划分析

1. 实际执行时间分析(MySQL 8.0+)

sql

EXPLAIN ANALYZE

SELECT * FROM products

WHERE category_id = 101

AND price > 100

ORDER BY created_at DESC

LIMIT 10;


-- 输出结果:

/*

-> Limit: 10 row(s)  (actual time=15.25..15.30 rows=10 loops=1)

   -> Sort: products.created_at DESC, limit input to 10 row(s)  (actual time=15.24..15.28 rows=10 loops=1)

       -> Filter: (products.price > 100)  (actual time=0.125..14.50 rows=1500 loops=1)

           -> Index range scan on products using idx_category over (category_id = 101)  (actual time=0.120..12.35 rows=5000 loops=1)

*/


-- 关键指标分析:

-- actual time: 实际执行时间(15.30ms)

-- rows: 实际处理行数(1500行)

-- loops: 循环次数

2. 性能瓶颈定位

sql

EXPLAIN ANALYZE

SELECT u.username, COUNT(o.order_id) as order_count

FROM users u

LEFT JOIN orders o ON u.id = o.user_id

WHERE u.created_at > '2023-01-01'

GROUP BY u.id

HAVING order_count > 5;


-- 分析输出发现:

-- 1.  users表扫描: 12.5ms (扫描5000行)

-- 2.  orders表关联: 85.3ms (嵌套循环5000次)

-- 3.  分组操作: 45.2ms (临时表处理)


-- 优化方案:

-- • 为users.created_at添加索引

-- • 为orders.user_id添加索引  

-- • 考虑预聚合或缓存策略


六、高级技巧:EXPLAIN优化实战

1. 索引优化策略

sql

-- 检查索引使用效率

EXPLAIN

SELECT * FROM orders

WHERE user_id = 1001

AND order_date BETWEEN '2023-01-01' AND '2023-12-31'

AND status = 'completed';


-- 如果key_len过长,说明索引效率不高

-- 优化:创建更精准的复合索引

CREATE INDEX idx_user_date_status ON orders(user_id, order_date, status);


-- 验证优化效果

EXPLAIN

SELECT user_id, order_date, status

FROM orders

WHERE user_id = 1001

AND order_date > '2023-01-01';

-- Extra: Using index (覆盖索引)

2. 连接优化策略

sql

-- 分析连接顺序优化

EXPLAIN

SELECT * FROM table1 t1

JOIN table2 t2 ON t1.id = t2.t1_id

JOIN table3 t3 ON t2.id = t3.t2_id

WHERE t1.name = 'test' AND t3.value > 100;


-- 如果连接顺序不合理,使用STRAIGHT_JOIN强制顺序

EXPLAIN

SELECT STRAIGHT_JOIN * FROM table1 t1

JOIN table2 t2 ON t1.id = t2.t1_id

JOIN table3 t3 ON t2.id = t3.t2_id

WHERE t1.name = 'test' AND t3.value > 100;

3. 子查询优化

sql

-- 分析子查询执行计划

EXPLAIN

SELECT * FROM users

WHERE id IN (

   SELECT user_id FROM orders

   WHERE amount > 1000 AND status = 'completed'

);


-- 如果子查询性能差,考虑改写为JOIN

EXPLAIN

SELECT DISTINCT u.*

FROM users u

JOIN orders o ON u.id = o.user_id

WHERE o.amount > 1000 AND o.status = 'completed';


-- 或者使用EXISTS

EXPLAIN

SELECT * FROM users u

WHERE EXISTS (

   SELECT 1 FROM orders o

   WHERE o.user_id = u.id

   AND o.amount > 1000

   AND o.status = 'completed'

);


七、常见EXPLAIN问题与解决方案

1. 性能问题诊断表

EXPLAIN现象 可能问题 解决方案
type=ALL 缺少索引 添加合适的索引
Using filesort 排序字段无索引 添加排序索引或优化查询
Using temporary 需要临时表 优化GROUP BY或DISTINCT
rows值过大 索引选择性差 优化索引或查询条件
key=NULL 未使用索引 检查查询条件或索引定义

2. 索引失效场景分析

sql

-- 1. 函数导致索引失效

EXPLAIN SELECT * FROM users WHERE DATE(created_at) = '2023-01-01';

-- 优化:使用范围查询

EXPLAIN SELECT * FROM users WHERE created_at >= '2023-01-01' AND created_at < '2023-01-02';


-- 2. 隐式类型转换

EXPLAIN SELECT * FROM users WHERE phone = 13800138000; -- phone是varchar

-- 优化:使用正确类型

EXPLAIN SELECT * FROM users WHERE phone = '13800138000';


-- 3. 前导通配符

EXPLAIN SELECT * FROM users WHERE name LIKE '%john%';

-- 优化:使用全文索引或改写查询


八、EXPLAIN最佳实践指南

1. 日常优化流程

sql

-- 步骤1:识别慢查询

SELECT * FROM mysql.slow_log ORDER BY start_time DESC LIMIT 10;


-- 步骤2:使用EXPLAIN分析

EXPLAIN FORMAT=JSON

-- 粘贴慢查询SQL here


-- 步骤3:优化索引和查询

-- 根据EXPLAIN结果添加索引、重写查询


-- 步骤4:验证优化效果

EXPLAIN ANALYZE

-- 优化后的SQL here


-- 步骤5:监控持续性能

SHOW STATUS LIKE 'Handler_read%';

2. 自动化优化脚本

sql

-- 自动分析常用查询

SELECT

   query,

   EXPLAIN_FORMAT = JSON_EXTRACT(EXPLAIN_FORMAT, '$.query_block.cost_info.query_cost') as cost

FROM (

   SELECT

       sql_text AS query,

       EXPLAIN_FORMAT = (EXPLAIN FORMAT=JSON sql_text)

   FROM performance_schema.events_statements_summary_by_digest

   WHERE digest_text LIKE 'SELECT%'

   ORDER BY sum_timer_wait DESC

   LIMIT 10

) AS top_queries

ORDER BY cost DESC;


九、总结:成为EXPLAIN专家

1. 核心技能掌握

  • 快速解读:10秒内看懂EXPLAIN输出
  • 问题诊断:准确识别性能瓶颈
  • 优化方案:提出有效的优化策略
  • 效果验证:量化优化成果

2. 性能优化等级

等级 能力要求 典型优化效果
初级 看懂type和key字段 2-10倍性能提升
中级 分析Extra和rows字段 10-100倍性能提升
高级 使用JSON格式深度优化 100倍以上性能提升
专家 EXPLAIN ANALYZE实战 系统级性能优化

3. 持续学习建议

  1. 实践为主:每天分析1-2个真实查询
  2. 深度理解:研究MySQL源码执行机制
  3. 工具链完善:结合pt-query-digest等工具
  4. 持续监控:建立性能基线并持续优化

通过本文的深度解析,你现在已经掌握了EXPLAIN这个MySQL优化利器。记住:真正的优化大师不是靠猜测,而是靠数据说话。现在就开始使用EXPLAIN分析你的查询,让性能优化变得科学而高效!

相关实践学习
每个IT人都想学的“Web应用上云经典架构”实战
本实验从Web应用上云这个最基本的、最普遍的需求出发,帮助IT从业者们通过“阿里云Web应用上云解决方案”,了解一个企业级Web应用上云的常见架构,了解如何构建一个高可用、可扩展的企业级应用架构。
MySQL数据库入门学习
本课程通过最流行的开源数据库MySQL带你了解数据库的世界。 &nbsp; 相关的阿里云产品:云数据库RDS MySQL 版 阿里云关系型数据库RDS(Relational Database Service)是一种稳定可靠、可弹性伸缩的在线数据库服务,提供容灾、备份、恢复、迁移等方面的全套解决方案,彻底解决数据库运维的烦恼。 了解产品详情:&nbsp;https://www.aliyun.com/product/rds/mysql&nbsp;
相关文章
|
2月前
|
SQL 监控 关系型数据库
mysql 索引失效?怎么解决? (重点知识,建议收藏,读10遍+)
本文总结MySQL索引失效的八大常见场景,如函数操作、类型不匹配、OR连接、违背最左前缀等,并结合实际案例解析。通过EXPLAIN分析执行计划,帮助开发者识别问题,提供优化策略,提升查询性能。
|
3月前
|
SQL 存储 关系型数据库
MySQL体系结构详解:一条SQL查询的旅程
本文深入解析MySQL内部架构,从SQL查询的执行流程到性能优化技巧,涵盖连接建立、查询处理、执行阶段及存储引擎工作机制,帮助开发者理解MySQL运行原理并提升数据库性能。
|
4月前
|
设计模式 XML 安全
Java枚举(Enum)与设计模式应用
Java枚举不仅是类型安全的常量,还具备面向对象能力,可添加属性与方法,实现接口。通过枚举能优雅实现单例、策略、状态等设计模式,具备线程安全、序列化安全等特性,是编写高效、安全代码的利器。
|
3月前
|
SQL 监控 关系型数据库
SQL优化技巧:让MySQL查询快人一步
本文深入解析了MySQL查询优化的核心技巧,涵盖索引设计、查询重写、分页优化、批量操作、数据类型优化及性能监控等方面,帮助开发者显著提升数据库性能,解决慢查询问题,适用于高并发与大数据场景。
|
12月前
|
SQL 关系型数据库 MySQL
深入解析MySQL的EXPLAIN:指标详解与索引优化
MySQL 中的 `EXPLAIN` 语句用于分析和优化 SQL 查询,帮助你了解查询优化器的执行计划。本文详细介绍了 `EXPLAIN` 输出的各项指标,如 `id`、`select_type`、`table`、`type`、`key` 等,并提供了如何利用这些指标优化索引结构和 SQL 语句的具体方法。通过实战案例,展示了如何通过创建合适索引和调整查询语句来提升查询性能。
2494 10
|
3月前
|
XML Java 数据格式
Bean的生命周期:从Spring的子宫到坟墓
Spring 管理 Bean 的生命周期,从对象注册、实例化、属性注入、初始化、使用到销毁,全程可控。Bean 的创建基于配置或注解,Spring 在容器启动时扫描并生成 BeanDefinition,按需实例化并填充依赖。通过 Aware 回调、初始化方法、AOP 代理等机制,实现灵活扩展。了解 Bean 生命周期有助于更好地掌握 Spring 框架运行机制,提升开发效率与系统可维护性。
|
消息中间件 存储 中间件
【消息中间件】详解三大MQ:RabbitMQ、RocketMQ、Kafka
【消息中间件】详解三大MQ:RabbitMQ、RocketMQ、Kafka
12387 1
|
6月前
|
固态存储 关系型数据库 数据库
从Explain到执行:手把手优化PostgreSQL慢查询的5个关键步骤
本文深入探讨PostgreSQL查询优化的系统性方法,结合15年数据库优化经验,通过真实生产案例剖析慢查询问题。内容涵盖五大关键步骤:解读EXPLAIN计划、识别性能瓶颈、索引优化策略、查询重写与结构调整以及系统级优化配置。文章详细分析了慢查询对资源、硬件成本及业务的影响,并提供从诊断到根治的全流程解决方案。同时,介绍了索引类型选择、分区表设计、物化视图应用等高级技巧,帮助读者构建持续优化机制,显著提升数据库性能。最终总结出优化大师的思维框架,强调数据驱动决策与预防性优化文化,助力优雅设计取代复杂补救,实现数据库性能质的飞跃。
917 0
|
4月前
|
存储 缓存 Java
Java数组全解析:一维、多维与内存模型
本文深入解析Java数组的内存布局与操作技巧,涵盖一维及多维数组的声明、初始化、内存模型,以及数组常见陷阱和性能优化。通过图文结合的方式帮助开发者彻底理解数组本质,并提供Arrays工具类的实用方法与面试高频问题解析,助你掌握数组核心知识,避免常见错误。
|
3月前
|
SQL 存储 关系型数据库
MySQL索引原理:B+树为什么是数据库的首选
MySQL为何选择B+树作为索引结构?本文深入解析B+树的底层机制,通过对比哈希表、二叉树、B树等数据结构,揭示其在磁盘I/O效率、范围查询和数据稳定性方面的优势。内容涵盖B+树的核心原理、在MySQL中的实现、性能优化策略及实际业务场景应用,帮助你深入理解索引背后的运作原理,从而优化数据库查询性能。

热门文章

最新文章