MySQL EXPLAIN 解读

在 MySQL 里,EXPLAIN是一个很实用的语句,主要用于分析 SQL 查询的执行计划。通过它,你能够了解到 MySQL 是怎样处理查询语句的,这有助于优化查询性能、分析索引使用情况。

一、基本用法

sql 复制代码
EXPLAIN SELECT * FROM users WHERE age > 25;

支持**SELECTINSERTUPDATEDELETE** 等语句;MySQL 8.0+ 支持**EXPLAIN ANALYZE**获取更详细执行数据。

示例:

二、关键字段解析

1、id

该语句的唯一标识。如果explain的结果包括多个id值,则数字越大越先执行;而对于相同id的行,则表示从上往下依次执行。

2、select_type

查询类型:

simple:简单select(不使用union或子查询)。

primary:最外面的select。

union:union中的第二个或后面的select语句。

dependent union:union中的第二个或后面的select语句,取决于外面的查询。

union result:union的结果。

subquery:子查询中的第一个select。

dependent subquery:子查询中的第一个select,取决于外面的查询。

derived:导出表的select(from子句的子查询)。

3、table

当前的表

4、type

type指查询使用了哪种类型,反映了 MySQL 如何查找表中的行。连接类型的效率从高到低排序如下:

4.1. system:表中只有一行数据(系统表),这是效率最高的连接类型。

4.2. const:通过索引一次就能找到数据,通常用于主键或唯一索引的等值查询。

sql 复制代码
EXPLAIN SELECT * FROM users WHERE id = 1;

+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| 1  | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const | 1    |       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+

4.3. eq_ref:对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引的关联查询。

sql 复制代码
-- 假设orders表的user_id字段关联users表的主键
EXPLAIN SELECT * FROM users 
JOIN orders ON users.id = orders.user_id 
WHERE users.id = 1;
+----+-------------+-------+--------+---------------+---------+---------+----------------+------+-------+
| id | select_type | table | type   | possible_keys | key     | key_len | ref            | rows | Extra |
+----+-------------+-------+--------+---------------+---------+---------+----------------+------+-------+
| 1  | SIMPLE      | users | const  | PRIMARY       | PRIMARY | 4       | const          | 1    |       |
| 1  | SIMPLE      | orders| eq_ref | user_id       | user_id | 4       | test.users.id  | 1    |       |
+----+-------------+-------+--------+---------------+---------+---------+----------------+------+-------+

4.4. ref:使用非唯一索引或唯一索引的前缀进行查找,返回匹配某个值的所有行。

sql 复制代码
-- 假设name字段有普通索引
EXPLAIN SELECT * FROM users WHERE name = 'Alice';
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
| id | select_type | table | type | possible_keys | key  | key_len | ref   | rows | Extra |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+
| 1  | SIMPLE      | users | ref  | idx_name      | idx_name | 767     | const | 10   |       |
+----+-------------+-------+------+---------------+------+---------+-------+------+-------+

4.5. range :只检索给定范围的行,使用一个索引来选择行,常见于 WHERE 子句中的 ><BETWEEN 等操作。

sql 复制代码
EXPLAIN SELECT * FROM products WHERE price BETWEEN 100 AND 200;

+----+-------------+----------+-------+---------------+-----------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys | key       | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------+-----------+---------+------+------+-------------+
| 1  | SIMPLE      | products | range | idx_price     | idx_price | 8       | NULL | 500  | Using where |
+----+-------------+----------+-------+---------------+-----------+---------+------+------+-------------+

4.6. index: 全索引扫描,与 ALL 类似,但只扫描索引树,通常比 ALL 快,因为索引文件通常比数据文件小。

sql 复制代码
-- 假设name字段有索引,查询只需要name字段
EXPLAIN SELECT name FROM users;
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type  | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+
| 1  | SIMPLE      | users | index | NULL          | idx_name | 767     | NULL | 1000 | Using index |
+----+-------------+-------+-------+---------------+------+---------+------+------+-------------+

**4.7. ALL:**全表扫描,MySQL 必须遍历全表来找到匹配的行,这是效率最低的连接类型。

sql 复制代码
EXPLAIN SELECT * FROM users WHERE age > 30;
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
| 1  | SIMPLE      | users | ALL  | NULL          | NULL | NULL    | NULL | 1000 | Using where |
+----+-------------+-------+------+---------------+------+---------+------+------+-------------+
5、possible_keys

显示 MySQL 在执行查询时,理论上可能使用的索引。这些索引基于查询条件(如 WHERE 子句、JOIN 条件)中的字段。注意:该字段仅列出可能的索引,不代表实际使用。

6、key

显示 MySQL 在实际执行查询时选择使用的索引。如果为 NULL,表示未使用任何索引(可能是全表扫描)。

MySQL 会根据索引选择性、统计信息和执行成本,从 possible_keys 中选择最优索引。

7、key_len

显示 MySQL 在查询中使用的索引字段的总长度,包括可能的 NULL 值标记位和字符集占用的字节数。

8、ref

示了在执行查询时,哪些值被用于与索引进行匹配。

这些值可以是:常量(如 const)其他表的列(如 table_name.column_name)表达式

当查询条件为常量(如 WHERE id = 1)且使用主键或唯一索引时,ref 显示为 const

sql 复制代码
EXPLAIN SELECT * FROM users WHERE id = 100;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| 1  | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const | 1    |       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+

JOIN 查询中,当使用一个表的列与另一个表的索引进行匹配时,ref 显示为关联表的列名。

sql 复制代码
EXPLAIN SELECT * 
FROM orders 
JOIN users ON orders.user_id = users.id 
WHERE users.id = 100;
+----+-------------+--------+--------+---------------+---------+---------+----------------+------+-------+
| id | select_type | table  | type   | possible_keys | key     | key_len | ref            | rows | Extra |
+----+-------------+--------+--------+---------------+---------+---------+----------------+------+-------+
| 1  | SIMPLE      | users  | const  | PRIMARY       | PRIMARY | 4       | const          | 1    |       |
| 1  | SIMPLE      | orders | ref    | user_id       | user_id | 4       | test.users.id  | 5    |       |
+----+-------------+--------+--------+---------------+---------+---------+----------------+------+-------+

当查询条件使用函数或表达式时,ref 显示为 func

sql 复制代码
EXPLAIN SELECT * FROM users WHERE YEAR(created_at) = 2023;
+----+-------------+-------+------+------------------+------------------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys    | key              | key_len | ref  | rows | Extra       |
+----+-------------+-------+------+------------------+------------------+---------+------+------+-------------+
| 1  | SIMPLE      | users | ref  | idx_created_at   | idx_created_at   | 5       | func | 100  | Using where |
+----+-------------+-------+------+------------------+------------------+---------+------+------+-------------+
9、rows

是 MySQL 优化器根据统计信息估算的,为了执行查询而需要扫描的行数。这个值是一个估算值,并非精确值。

10、filtered

表示 MySQL 优化器估算的,在扫描了 rows 行数据后,最终满足 WHERE 子句条件的行的百分比。

取值范围:0.00%(无匹配)到 100.00%(全部匹配)

例:

rows = 1:通过主键精确匹配,仅需扫描 1 行。

filtered = 100.00%:主键查询精确匹配,所有扫描行均满足条件。

sql 复制代码
EXPLAIN SELECT * FROM users WHERE id = 100;
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| id | select_type | table | type  | possible_keys | key     | key_len | ref   | rows | Extra |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
| 1  | SIMPLE      | users | const | PRIMARY       | PRIMARY | 4       | const | 1    |       |
+----+-------------+-------+-------+---------------+---------+---------+-------+------+-------+
11、Extra

Extra 是一个非常关键的字段,它提供了关于 MySQL 执行查询时的额外细节信息 ,补充了其他字段(如 keyrows 等)未涵盖的执行逻辑,是判断查询性能瓶颈的重要依据。

Extra 字段会显示 MySQL 如何处理查询的具体方式,例如是否使用了索引覆盖、是否需要临时表、是否需要额外排序、是否进行了全表扫描等。这些信息能直接反映查询的效率高低,帮助我们优化 SQL(比如调整索引、改写查询逻辑)。

理想情况(高效执行)

Using index

表示查询使用了覆盖索引 (Covering Index),即查询所需的所有字段(SELECT 后的列、WHERE 条件列等)都包含在某个索引中,MySQL 无需回表查询数据行,直接通过索引即可获取结果。

Using where; Using index

表示查询既使用了索引(Using index),又通过索引过滤了数据(Using where),但仍无需回表。

Using index condition

表示使用了索引条件下推(Index Condition Pushdown, ICP) 优化。MySQL 会在存储引擎层(而非服务器层)就用索引过滤部分数据,减少回表次数。

Range

表示查询使用了索引的范围扫描 (如 WHERE id BETWEEN 1 AND 100WHERE name LIKE '张%'),仅扫描索引中符合范围的部分,而非全索引扫描。

需要优化的情况(低效执行)

Using filesort

表示 MySQL 需要对结果进行额外的排序操作(排序未通过索引完成)。

Using temporary

表示 MySQL 需要创建临时表 来存储中间结果(通常用于 GROUP BYDISTINCT 或多表连接时)。

Using where

单独出现时,表示 MySQL 使用了索引查找数据,但索引无法直接过滤所有条件,需要在获取数据行后,再通过 WHERE 条件进一步过滤(可能需要回表)。

Using join buffer

表示多表连接时,MySQL 未使用索引连接,而是使用了连接缓冲区(Join Buffer)存储中间结果。

Using full scan

表示查询进行了全表扫描(Full Table Scan),未使用任何索引。

Impossible WHERE

表示 WHERE 条件永远为 FALSE,MySQL 无需执行任何查询(直接返回空结果)。

Range checked for each record (index map: N)

表示 MySQL 无法确定使用哪个索引,只能对前一张表的每一行,都尝试检查是否有合适的索引可用(通常发生在多表连接且索引不明确时)。

相关推荐
xiao-xiang6 小时前
redis-保姆级配置详解
数据库·redis
白鹭7 小时前
MySQL(多表查询练习)
数据库·mysql
xiao-xiang11 小时前
redis-sentinel基础概念及部署
数据库·redis·sentinel
AI 嗯啦12 小时前
SQL详细语法教程(三)mysql的函数知识
android·开发语言·数据库·python·sql·mysql
时序数据说12 小时前
国内时序数据库概览
大数据·数据库·物联网·时序数据库·iotdb
杰克尼13 小时前
mysql-条件查询案例
数据库·mysql
你那是什么调调15 小时前
`SHOW PROCESSLIST;` 返回列详解(含义 + 单位)
mysql
运维行者_15 小时前
使用Applications Manager进行 Apache Solr 监控
运维·网络·数据库·网络安全·云计算·apache·solr
云心雨禅16 小时前
网站突然崩了,此站点遇到了致命错误!
服务器·mysql