EXPLAIN字段详解
准备工作
- Ubuntu 22.04
- mysql 8.0.37
- 实验数据
DROP DATABASE IF EXISTS `study`;
CREATE DATABASE `study` DEFAULT CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
-- 切换到study数据库
USE `study`;
-- 课程表
DROP TABLE IF EXISTS `course`;
CREATE TABLE `course` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- mock数据
INSERT INTO `course` (`id`, `name`) VALUES
(1, '数学'),
(2, '语文'),
(3, '英语'),
(4, '物理'),
(5, '化学'),
(6, '生物');
-- 学生选课表
DROP TABLE IF EXISTS `student_course`;
CREATE TABLE `student_course` (
`id` int NOT NULL AUTO_INCREMENT,
`student_id` int NOT NULL,
`course_id` int NOT NULL,
PRIMARY KEY (`id`),
KEY `idx_student_id` (`student_id`),
KEY `idx_course_id` (`course_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- mock数据
INSERT INTO `student_course` (`id`, `student_id`, `course_id`) VALUES
(1, 1, 1),
(2, 1, 2),
(3, 2, 1),
(4, 2, 3),
(5, 3, 4),
(6, 3, 5),
(7, 4, 6);
-- 学生表
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student` (
`id` int NOT NULL AUTO_INCREMENT,
`name` varchar(50) NOT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;
-- mock数据
INSERT INTO `student` (`id`, `name`) VALUES
(1, '小明'),
(2, '小红'),
(3, '小刚'),
(4, '小丽');
1. 什么是EXPLAIN?
EXPLAIN是MySQL提供的一个命令,用于分析SQL查询语句的执行计划。通过EXPLAIN,我们可以了解MySQL是如何处理查询的,包括使用了哪些索引、表的连接顺序等信息,从而帮助我们优化查询性能。
2. EXPLAIN的使用方法
在MySQL中,可以通过在查询语句前加上EXPLAIN关键字来查看执行计划。例如:
mysql> use study;
Database changed
mysql> explain select * from student;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 100.00 | NULL |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
3. EXPLAIN字段详解
EXPLAIN的输出结果包含多个字段,下面是对每个字段的详细解释:
| 字段名 | 说明 |
|---|---|
| id | 查询的唯一标识符,表示查询的顺序。 |
| select_type | 查询的类型,如SIMPLE(简单查询)、PRIMARY(主查询)、UNION等。 |
| table | 正在访问的表的名称。 |
| partitions | 使用的分区信息,如果没有使用分区则为NULL。 |
| type | 连接类型,表示MySQL如何查找表中的行。常见的类型有ALL、index、range、ref、eq_ref、const、system等。 |
| possible_keys | 可能使用的索引,一个或多个,不一定会被实际使用。 |
| key | 实际使用的索引,如果没有使用索引则为NULL。 |
| key_len | 使用的索引长度,表示MySQL在查询中使用了多少字节的索引。 |
| ref | 显示索引的哪一列被使用了,或者是常量,表示被用于查找索引列上的值。 |
| rows | 根据统计信息及索引选用情况,大致估算出找到所需记录需要扫描的行数。 |
| filtered | 估算的过滤率,表示在扫描的行中有多少百分比会被返回。 |
| Extra | 额外信息,提供了关于查询执行的更多细节,如Using where、Using index等。 |
4. id与table字段
在EXPLAIN的输出中,id字段和table字段可以帮助我们理解查询的执行顺序和涉及的表。id字段越大,执行的优先级越高,在id字段相同的情况下,按照table字段的顺序从上到下执行。
4.1 id字段相同
例如:
mysql> use study;
Database changed
mysql> explain select s.id, s.name, c.name from student s join student_course sc on s.id = sc.student_id join course c on sc.course_id = c.id;
+----+-------------+-------+------------+--------+------------------------------+----------------+---------+--------------------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+--------+------------------------------+----------------+---------+--------------------+------+----------+-------+
| 1 | SIMPLE | s | NULL | ALL | PRIMARY | NULL | NULL | NULL | 4 | 100.00 | NULL |
| 1 | SIMPLE | sc | NULL | ref | idx_student_id,idx_course_id | idx_student_id | 4 | study.s.id | 1 | 100.00 | NULL |
| 1 | SIMPLE | c | NULL | eq_ref | PRIMARY | PRIMARY | 4 | study.sc.course_id | 1 | 100.00 | NULL |
+----+-------------+-------+------------+--------+------------------------------+----------------+---------+--------------------+------+----------+-------+
3 rows in set, 1 warning (0.00 sec)
这条sql的作用是将student表、student_course表和course表进行连接查询,获取学生的ID、姓名以及所选课程的名称。
在这个例子中,id字段都为1,表示这些表是同一查询的一部分。执行顺序是先访问student表(s),然后是student_course表(sc),最后是course表©。
4.2 id字段不同时
当id字段不同时,id值较大的查询会先执行。例如:
mysql> use study;
Database changed
mysql> explain select s.name from student s where s.id = (select sc.student_id from student_course sc where sc.course_id = (select c.id from course c where c.name = '数学' order by c.id limit 1) order by sc.student_id limit 1);
+----+-------------+-------+------------+-------+---------------+----------------+---------+-------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+-------+---------------+----------------+---------+-------+------+----------+-------------+
| 1 | PRIMARY | s | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 2 | SUBQUERY | sc | NULL | index | idx_course_id | idx_student_id | 4 | NULL | 3 | 28.57 | Using where |
| 3 | SUBQUERY | c | NULL | index | NULL | PRIMARY | 4 | NULL | 1 | 16.67 | Using where |
+----+-------------+-------+------------+-------+---------------+----------------+---------+-------+------+----------+-------------+
3 rows in set, 1 warning (0.00 sec)
这条sql的作用首先查询了course表©中名称为’数学’的最小课程ID,然后根据这个ID查询student_course表(sc)中对应的学生ID,最后再根据学生ID查询student表(s)中学生的姓名。
在这个例子中,id字段为1的查询是主查询,id字段为2和3的查询是子查询。执行顺序是先执行id为3的子查询,然后是id为2的子查询,最后执行主查询。
5. select_type字段
select_type字段表示查询的类型,常见的类型有:
5.1 SIMPLE:简单查询,不包含子查询或UNION。
如4.1节中的查询。
5.2 PRIMARY:主查询,表示这是一个最外层的查询。
如4.2节中的主查询。
5.3 UNION:UNION查询,当一个查询在UNION关键字后面时,表示这是一个UNION查询的一部分。
mysql> use study;
Database changed
mysql> explain select * from student where id = 1 union select * from student where id = 2;
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
| 1 | PRIMARY | student | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 2 | UNION | student | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
| 3 | UNION RESULT | <union1,2> | NULL | ALL | NULL | NULL | NULL | NULL | NULL | NULL | Using temporary |
+----+--------------+------------+------------+-------+---------------+---------+---------+-------+------+----------+-----------------+
3 rows in set, 1 warning (0.00 sec)
这条sql的作用是查询学生ID为1和2的学生信息,并将结果合并。可以看到id为1的查询是主查询,id为2的查询是UNION的一部分,id为3的查询是UNION结果。
5.4 SUBQUERY:子查询,表示这是一个嵌套的查询。
如4.2节中的子查询。
6. type字段(重点!!!)
type字段表示MySQL在查询中使用的连接类型,连接类型的好坏直接影响查询性能。下面从好到坏列出常见的连接类型:
6.1 NULL:能直接在优化阶段分解查询语句,在执行阶段不再访问表或索引。
mysql> use study;
Database changed
mysql> EXPLAIN SELECT MAX(id) FROM student;
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
| 1 | SIMPLE | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | Select tables optimized away |
+----+-------------+-------+------------+------+---------------+------+---------+------+------+----------+------------------------------+
1 row in set, 1 warning (0.00 sec)
这条SQL查询学生表中ID的最大值,MySQL在优化阶段就能计算出结果,因此不需要访问表或索引,前提是查询的字段是索引字段。
6.2 system:表只有一行记录,MySQL直接返回该行,属于const类型的特殊情况。
mysql> use study;
Database changed
mysql> EXPLAIN SELECT name FROM (SELECT * FROM student WHERE id = 1 ORDER BY id LIMIT 1) AS t;
+----+-------------+------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
| 1 | PRIMARY | <derived2> | NULL | system | NULL | NULL | NULL | NULL | 1 | 100.00 | NULL |
| 2 | DERIVED | student | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+------------+------------+--------+---------------+---------+---------+-------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)
这条SQL查询学生表中ID为1的学生姓名,子查询返回的结果只有一行,因此MySQL将主查询的连接类型标记为system。
6.3 const:表示通过主键或唯一索引查找单行数据,MySQL在执行阶段只需要访问一次表。
mysql> use study;
Database changed
mysql> EXPLAIN SELECT name FROM student WHERE id = 1;
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| 1 | SIMPLE | student | NULL | const | PRIMARY | PRIMARY | 4 | const | 1 | 100.00 | NULL |
+----+-------------+---------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
这条SQL查询学生表中ID为1的学生姓名,MySQL通过主键索引直接定位到该行数据,连接类型为const。
6.4 eq_ref:表示通过唯一索引或主键查找数据,通常用于连接查询。多表join时,对于来自前面表的每一行,在当前表中只会返回一行数据。
如4.1节中表c的查询。
6.5 ref:用于单表扫描或连接。表示单表扫描时,通过非唯一索引查找数据。表示连接时,驱动表中的一条记录能在被驱动表中通过非唯一索引找到多条记录。
6.5.1 单表扫描
mysql> use study;
Database changed
mysql> EXPLAIN SELECT * FROM student_course WHERE course_id = 1;
+----+-------------+----------------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
| 1 | SIMPLE | student_course | NULL | ref | idx_course_id | idx_course_id | 4 | const | 2 | 100.00 | NULL |
+----+-------------+----------------+------------+------+---------------+---------------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
这条SQL查询student_course表中课程ID为1的记录,MySQL通过非唯一索引idx_course_id查找数据,连接类型为ref。
6.5.2 多表连接
如4.1节中表sc的查询。
6.6 range:表示通过索引范围扫描查找数据,通常用于BETWEEN、<、>、IN、LIKE等操作符。
mysql> use study;
Database changed
mysql> EXPLAIN SELECT * FROM student WHERE id IN (1, 2);
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
| 1 | SIMPLE | student | NULL | range | PRIMARY | PRIMARY | 4 | NULL | 2 | 100.00 | Using where |
+----+-------------+---------+------------+-------+---------------+---------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
这条SQL查询学生表中ID为1或2的记录,MySQL通过主键索引范围扫描查找数据,连接类型为range。
6.7 index_merge:表示通过多个索引的交集或并集查找数据。
mysql> use study;
Database changed
mysql> EXPLAIN SELECT * FROM student_course WHERE student_id = 1 OR course_id = 1;
+----+-------------+----------------+------------+-------------+------------------------------+------------------------------+---------+------+------+----------+--------------------------------------------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+-------------+------------------------------+------------------------------+---------+------+------+----------+--------------------------------------------------------+
| 1 | SIMPLE | student_course | NULL | index_merge | idx_student_id,idx_course_id | idx_student_id,idx_course_id | 4,4 | NULL | 4 | 100.00 | Using union(idx_student_id,idx_course_id); Using where |
+----+-------------+----------------+------------+-------------+------------------------------+------------------------------+---------+------+------+----------+--------------------------------------------------------+
1 row in set, 1 warning (0.00 sec)
这条SQL查询student_course表中学生ID为1或课程ID为1的记录,MySQL通过索引idx_student_id和idx_course_id的并集查找数据,连接类型为index_merge。
6.8 index:表示通过索引全扫描查找数据,类似于ALL,但扫描的是索引而不是表。
mysql> use study;
Database changed
mysql> EXPLAIN SELECT id FROM student_course;
+----+-------------+----------------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
| 1 | SIMPLE | student_course | NULL | index | NULL | idx_student_id | 4 | NULL | 7 | 100.00 | Using index |
+----+-------------+----------------+------------+-------+---------------+----------------+---------+------+------+----------+-------------+
1 row in set, 1 warning (0.00 sec)
这条SQL查询student_course表中所有记录的ID,MySQL通过索引idx_student_id全扫描查找数据,连接类型为index。
6.9 ALL:表示全表扫描,MySQL需要扫描整个表来查找数据,通常是最慢的连接类型。
mysql> use study;
Database changed
mysql> EXPLAIN SELECT * FROM student;
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
| 1 | SIMPLE | student | NULL | ALL | NULL | NULL | NULL | NULL | 4 | 100.00 | NULL |
+----+-------------+---------+------------+------+---------------+------+---------+------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)
这条SQL查询学生表中的所有记录,MySQL需要扫描整个表来查找数据,连接类型为ALL。如果表非常大,可能会导致查询性能较差。
7. possible_keys和key字段
7.1 possible_keys:表示查询中可能使用的索引,一个或多个,不一定会被实际使用。如果想强制使用某个索引,可以在查询中使用FORCE INDEX、USE INDEX或IGNORE INDEX。
7.2 key:表示实际使用的索引,如果为NULL,表示没有使用索引,如果使用了索引覆盖,则只会出现在key字段中,并在Extra字段中显示Using index。
8. key_len字段
key_len字段表示使用的索引长度,单位为字节。它表示MySQL在查询中使用了多少字节的索引。这个值可以帮助我们了解索引的选择性和效率,通常情况下,较小的key_len值表示更高效的索引。
9. ref字段
ref字段显示索引的哪一列被使用了,或者是常量,表示被用于查找索引列上的值。它可以是以下几种情况:
- 常量:表示索引列与一个常量值进行比较,例如
const。 - 列名:表示索引列与另一个列进行比较,例如
study.s.id。 - NULL:表示没有使用索引。
10. rows字段
rows字段表示根据统计信息及索引选用情况,大致估算出找到所需记录需要扫描的行数。这个值是一个估算值,实际扫描的行数可能会有所不同。较小的rows值通常表示查询效率更高。
11. filtered字段
filtered字段表示估算的过滤率,表示在扫描的行中有多少百分比会被返回。这个值是一个百分比,范围从0到100。较高的filtered值通常表示查询效率更高,因为更多的行会被返回。
12. Extra字段
Extra字段提供了关于查询执行的更多细节,常见的值有:
- Using where:表示MySQL在查询中使用了WHERE条件来过滤结果。
- Using index:表示MySQL只通过索引就能满足查询条件,不需要访问表数据,通常表示索引覆盖。
- Using index condition:表示MySQL可以通过索引条件来过滤结果。
mysql> use study;
Database changed
mysql> EXPLAIN SELECT * FROM student_course WHERE student_id IN (1, 2);
+----+-------------+----------------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
| id | select_type | table | partitions | type | possible_keys | key | key_len | ref | rows | filtered | Extra |
+----+-------------+----------------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
| 1 | SIMPLE | student_course | NULL | range | idx_student_id | idx_student_id | 4 | NULL | 4 | 100.00 | Using index condition |
+----+-------------+----------------+------------+-------+----------------+----------------+---------+------+------+----------+-----------------------+
1 row in set, 1 warning (0.00 sec)
- Using temporary:表示MySQL需要使用临时表来存储中间结果,通常出现在
GROUP BY、ORDER BY等操作中。 - Using filesort:表示MySQL无法通过索引排序,需要额外的排序操作,通常会影响查询性能。
- Using join buffer:表示MySQL在连接操作中使用了连接缓冲区,通常出现在没有使用索引的连接操作中,通常会影响查询性能。
- Select tables optimized away:表示MySQL在优化阶段就能计算出结果,因此不需要访问表或索引。
13. 总结
通过EXPLAIN命令,我们可以详细了解MySQL查询的执行计划,从而帮助我们优化查询性能。重点关注type字段,尽量避免使用ALL连接类型,优先考虑使用const、eq_ref、ref等高效的连接类型。同时,合理设计索引,确保查询能够利用索引覆盖,减少全表扫描和临时表的使用,从而提升查询效率。
1983

被折叠的 条评论
为什么被折叠?



