MySQL EXPLAIN字段详解

EXPLAIN字段详解

准备工作

  1. Ubuntu 22.04
  2. mysql 8.0.37
  3. 实验数据
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_ididx_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_keyskey字段

7.1 possible_keys:表示查询中可能使用的索引,一个或多个,不一定会被实际使用。如果想强制使用某个索引,可以在查询中使用FORCE INDEXUSE INDEXIGNORE INDEX

7.2 key:表示实际使用的索引,如果为NULL,表示没有使用索引,如果使用了索引覆盖,则只会出现在key字段中,并在Extra字段中显示Using index。

8. key_len字段

key_len字段表示使用的索引长度,单位为字节。它表示MySQL在查询中使用了多少字节的索引。这个值可以帮助我们了解索引的选择性和效率,通常情况下,较小的key_len值表示更高效的索引。

9. ref字段

ref字段显示索引的哪一列被使用了,或者是常量,表示被用于查找索引列上的值。它可以是以下几种情况:

  1. 常量:表示索引列与一个常量值进行比较,例如const
  2. 列名:表示索引列与另一个列进行比较,例如study.s.id
  3. 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 BYORDER BY等操作中。
  • Using filesort:表示MySQL无法通过索引排序,需要额外的排序操作,通常会影响查询性能。
  • Using join buffer:表示MySQL在连接操作中使用了连接缓冲区,通常出现在没有使用索引的连接操作中,通常会影响查询性能。
  • Select tables optimized away:表示MySQL在优化阶段就能计算出结果,因此不需要访问表或索引。

13. 总结

通过EXPLAIN命令,我们可以详细了解MySQL查询的执行计划,从而帮助我们优化查询性能。重点关注type字段,尽量避免使用ALL连接类型,优先考虑使用const、eq_ref、ref等高效的连接类型。同时,合理设计索引,确保查询能够利用索引覆盖,减少全表扫描和临时表的使用,从而提升查询效率。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程小輝

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值