MySQL EXPLAIN 解读

发布于:2025-07-21 ⋅ 阅读:(17) ⋅ 点赞:(0)

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

一、基本用法

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:通过索引一次就能找到数据,通常用于主键或唯一索引的等值查询。

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:对于每个索引键,表中只有一条记录与之匹配,常见于主键或唯一索引的关联查询。

-- 假设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:使用非唯一索引或唯一索引的前缀进行查找,返回匹配某个值的所有行。

-- 假设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 等操作。

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 快,因为索引文件通常比数据文件小。

-- 假设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 必须遍历全表来找到匹配的行,这是效率最低的连接类型。

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

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 显示为关联表的列名。

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

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%:主键查询精确匹配,所有扫描行均满足条件。

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 100 或 WHERE 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 无法确定使用哪个索引,只能对前一张表的每一行,都尝试检查是否有合适的索引可用(通常发生在多表连接且索引不明确时)。


网站公告

今日签到

点亮在社区的每一天
去签到