详解mysql的explain,并分析id,select_type,table,type,possible_keys, Key,key_len,ref,rows,filtered,Extra的含义

使用explain的原因

在工作中,我们会开启慢查询,去记录一些执行时间比较久的SQL语句,继而对这些SQL语句进行分析,此时,便用到了explain这个命令来查看这些SQL语句的执行计划——该SQL语句有没有使用索引,有没有做全表扫描,这都可以通过explain命令来查看。

所以,我们深入了解MySQL的基于开销的优化器,还可以获得很多可能被优化器考虑到的访问策略的细节,以及当运行SQL语句时哪种策略预计会被优化器采用。

如何使用explain

使用explain非常简单,我们只要在SQL语句的前面加上explain即可,如代码explain select * from studentsexplain insert students(name) values('张三')等。

分析explain的字段

如下使用SQL查询学生和学生对应的班级,我们看下使用explain返回了哪些字段?

mysql> explain select * from students left join class on students.classid = class.id;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | students | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |   100.00 | NULL                                               |
|  1 | SIMPLE      | class    | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL |    2 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

我们明显地可以看到explain返回了这些字段:id ,select_type ,table ,partitions ,type ,possible_keys ,key ,key_len ,ref ,rows ,filtered ,Extra

如下我们便对这些字段,做单独分析。

id

包含一组数字,表示查询中执行select子句或操作表的顺序

id相同时,执行顺序由上至下

 explain select * from class where id in (select classid from students);
+----+-------------+----------+------------+-------+---------------+------------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table    | partitions | type  | possible_keys | key        | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+----------+------------+-------+---------------+------------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | students | NULL       | index | fk_classId    | fk_classId | 5       | NULL |    2 |    50.00 | Using index; LooseScan                             |
|  1 | SIMPLE      | class    | NULL       | ALL   | PRIMARY       | NULL       | NULL    | NULL |    2 |    50.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+----------+------------+-------+---------------+------------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

若为子查询,id的序号会递增,id值越大优先级越高,越先被执行

explain select * from class where id = (select distinct classid from students where classid =1);
+----+-------------+----------+------------+-------+---------------+------------+---------+-------+------+----------+-------------+
| id | select_type | table    | partitions | type  | possible_keys | key        | key_len | ref   | rows | filtered | Extra       |
+----+-------------+----------+------------+-------+---------------+------------+---------+-------+------+----------+-------------+
|  1 | PRIMARY     | class    | NULL       | const | PRIMARY       | PRIMARY    | 4       | const |    1 |   100.00 | NULL        |
|  2 | SUBQUERY    | students | NULL       | ref   | fk_classId    | fk_classId | 5       | const |    2 |   100.00 | Using index |
+----+-------------+----------+------------+-------+---------------+------------+---------+-------+------+----------+-------------+
2 rows in set, 1 warning (0.00 sec)

id如果相同,可认为一组,从上往下顺序执行;在所有组中,id值越大,优先级越高,越先执行

mysql> explain select * from class where id = (select distinct classid from students left join address on students.id = address.student_id );
+----+-------------+----------+------------+-------+---------------+------------+---------+-------+------+----------+--------------------------------------------------------------+
| id | select_type | table    | partitions | type  | possible_keys | key        | key_len | ref   | rows | filtered | Extra                                                        |
+----+-------------+----------+------------+-------+---------------+------------+---------+-------+------+----------+--------------------------------------------------------------+
|  1 | PRIMARY     | class    | NULL       | const | PRIMARY       | PRIMARY    | 4       | const |    1 |   100.00 | NULL                                                         |
|  2 | SUBQUERY    | students | NULL       | index | fk_classId    | fk_classId | 5       | NULL  |    2 |   100.00 | Using index; Using temporary                                 |
|  2 | SUBQUERY    | address  | NULL       | ALL   | NULL          | NULL       | NULL    | NULL  |    1 |   100.00 | Using where; Distinct; Using join buffer (Block Nested Loop) |
+----+-------------+----------+------------+-------+---------------+------------+---------+-------+------+----------+--------------------------------------------------------------+
3 rows in set, 1 warning (0.00 sec)

select_type

查询中每个select子句的类型

含义
SIMPLE简单SELECT,不使用UNION或子查询等
PRIMARY查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY
UNIONUNION中的第二个或后面的SELECT语句
DEPENDENT UNIONUNION中的第二个或后面的SELECT语句,取决于外面的查询
UNION RESULTUNION的结果
SUBQUERY子查询中的第一个SELECT
DEPENDENT SUBQUERY子查询中的第一个SELECT,取决于外面的查询
DERIVED派生表的SELECT, FROM子句的子查询
UNCACHEABLE SUBQUERY一个子查询的结果不能被缓存,必须重新评估外链接的第一行

比如上述SQL语句就涉及到 PRIMARY 和SUBQUERY 的查询。

table

显示当前行的数据是属于哪张表的,但有时不是真实的表名字,我们看到的是derivedx,如下所示:

mysql> EXPLAIN select * from class, (select distinct classid from students left join address on students.id = address.student_id) tmp where class.id = tmp.classid;
+----+-------------+------------+------------+-------+---------------+-------------+---------+---------------+------+----------+----------------------------------------------------+
| id | select_type | table      | partitions | type  | possible_keys | key         | key_len | ref           | rows | filtered | Extra                                              |
+----+-------------+------------+------------+-------+---------------+-------------+---------+---------------+------+----------+----------------------------------------------------+
|  1 | PRIMARY     | class      | NULL       | ALL   | PRIMARY       | NULL        | NULL    | NULL          |    2 |   100.00 | NULL                                               |
|  1 | PRIMARY     | <derived2> | NULL       | ref   | <auto_key0>   | <auto_key0> | 5       | test.class.id |    2 |   100.00 | Using index                                        |
|  2 | DERIVED     | students   | NULL       | index | fk_classId    | fk_classId  | 5       | NULL          |    2 |   100.00 | Using index; Using temporary                       |
|  2 | DERIVED     | address    | NULL       | ALL   | NULL          | NULL        | NULL    | NULL          |    1 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+------------+------------+-------+---------------+-------------+---------+---------------+------+----------+----------------------------------------------------+
4 rows in set, 1 warning (0.00 sec)

在第二步的时候就使用到了derived2这张表,但数据库并不存在这张表,因为我们在查询使用使用到了临时表,即如此SQL语句:(select distinct classid from students left join address on students.id = address.student_id) tmp

type

MySQL在表中找到所需行的方式,又称“访问类型”,常见类型:ALL,,index, range,ref,eq_ref, const, system,NULL,从左到右,性能从低到优。

含义
ALLFull Table Scan, MySQL将遍历全表以找到匹配的行
indexFull Index Scan,index与ALL区别为index类型只遍历索引树
range只检索给定范围的行,使用一个索引来选择行
ref表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
eq_ref类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件
const当MySQL对查询某部分进行优化,并转换为一个常量时,使用这些类型访问。如将主键置于where列表中,MySQL就能将该查询转换为一个常量
systemsystem是const类型的特例,当查询的表只有一行的情况下,使用system
NULLMySQL在优化过程中分解语句,执行时甚至不用访问表或索引,例如从一个索引列里选取最小值可以通过单独索引查找完成。
mysql> explain select * from students left join class on students.classid = class.id;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | students | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |   100.00 | NULL                                               |
|  1 | SIMPLE      | class    | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL |    2 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)


mysql> explain select * from students left join class on students.classid = class.id where students.id =1 and class.id =1;
+----+-------------+----------+------------+-------+--------------------+---------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type  | possible_keys      | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+-------+--------------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | students | NULL       | const | PRIMARY,fk_classId | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | class    | NULL       | const | PRIMARY            | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+----------+------------+-------+--------------------+---------+---------+-------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)

possible_keys

指出MySQL能使用表中的哪个索引找到记录,若查询的字段上存在索引,则该索引将被列出,但不一定被查询使用。

该列完全独立于explain输出所示表的次序,这意味着在possible_keys中的某些键,实际上不能按生成的表次序使用。

若该列是NULL,则没有相关的索引,可以通过检查where子句看是否引用某些列,或适合索引的列来提高你的查询性能。如果是这样,创造一个适当的索引并且再次用EXPLAIN检查查询。

explain select * from students left join class on students.classid = class.id;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | students | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |   100.00 | NULL                                               |
|  1 | SIMPLE      | class    | NULL       | ALL  | PRIMARY       | NULL | NULL    | NULL |    2 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

Key

key列显示MySQL实际决定使用的键(索引)

不选择索引,键值情况

若没有选择索引,键是NULL,比如

mysql> explain select * from class left join students on students.classid = class.id;
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
| id | select_type | table    | partitions | type | possible_keys | key  | key_len | ref  | rows | filtered | Extra                                              |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
|  1 | SIMPLE      | class    | NULL       | ALL  | NULL          | NULL | NULL    | NULL |    2 |   100.00 | NULL                                               |
|  1 | SIMPLE      | students | NULL       | ALL  | fk_classId    | NULL | NULL    | NULL |    2 |   100.00 | Using where; Using join buffer (Block Nested Loop) |
+----+-------------+----------+------------+------+---------------+------+---------+------+------+----------+----------------------------------------------------+
2 rows in set, 1 warning (0.00 sec)

选择索引,键值情况

虽然possible_keys 是fk_classId,但没有索引,若要使索引生效,可以这样写:

mysql> explain select * from class left join students on students.classid = class.id where students.classid =1;
+----+-------------+----------+------------+-------+---------------+------------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type  | possible_keys | key        | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------+------------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | class    | NULL       | const | PRIMARY       | PRIMARY    | 4       | const |    1 |   100.00 | NULL  |
|  1 | SIMPLE      | students | NULL       | ref   | fk_classId    | fk_classId | 5       | const |    2 |   100.00 | NULL  |
+----+-------------+----------+------------+-------+---------------+------------+---------+-------+------+----------+-------+
2 rows in set, 1 warning (0.00 sec)

若要想强制MySQL使用索引,或者忽视possible_keys列中的索引,在查询中使用FORCE INDEX、USE INDEX或者IGNORE INDEX。

key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引长度。

key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。

不损失精确度的情况下,长度越短越好 。

ref

列与索引的比较,表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值。

mysql> explain select * from students where id =1;
+----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
| id | select_type | table    | partitions | type  | possible_keys | key     | key_len | ref   | rows | filtered | Extra |
+----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
|  1 | SIMPLE      | students | NULL       | const | PRIMARY       | PRIMARY | 4       | const |    1 |   100.00 | NULL  |
+----+-------------+----------+------------+-------+---------------+---------+---------+-------+------+----------+-------+
1 row in set, 1 warning (0.00 sec)

rows

表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数

mysql> EXPLAIN select * from class, (select distinct classid from students ) tmp where class.id = tmp.classid;
+----+-------------+------------+------------+-------+---------------+-------------+---------+---------------+------+----------+-------------+
| id | select_type | table      | partitions | type  | possible_keys | key         | key_len | ref           | rows | filtered | Extra       |
+----+-------------+------------+------------+-------+---------------+-------------+---------+---------------+------+----------+-------------+
|  1 | PRIMARY     | class      | NULL       | ALL   | PRIMARY       | NULL        | NULL    | NULL          |    2 |   100.00 | NULL        |
|  1 | PRIMARY     | <derived2> | NULL       | ref   | <auto_key0>   | <auto_key0> | 5       | test.class.id |    2 |   100.00 | Using index |
|  2 | DERIVED     | students   | NULL       | index | fk_classId    | fk_classId  | 5       | NULL          |    2 |   100.00 | Using index |
+----+-------------+------------+------------+-------+---------------+-------------+---------+---------------+------+----------+-------------+
3 rows in set, 1 warning (0.04 sec)

filtered

过滤列表示按表条件过滤的表行的估计百分比。

最大值为 100,这意味着没有过滤行。 从 100 开始减小的值表示过滤量增加。

rows 显示检查的估计行数, rows × filters 显示与下表连接的行数。 例如,如果 rows 为 1000,filtered 为 50.00 (50%),则要与下表连接的行数为 1000 × 50% = 500。

Extra

该列包含MySQL解决查询的详细信息,有以下几种情况:

含义
Using where列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤
Using temporary表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询
Using filesortMySQL中无法利用索引完成的排序操作称为“文件排序”
Using join buffer该值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。
Impossible where这个值强调了where语句会导致没有符合条件的行。
Select tables optimized away这个值意味着仅通过使用索引,优化器可能仅从聚合函数结果中返回一行
using index指示查询优化器使用其中一个命名索引来查找表中的行
mysql> EXPLAIN select * from students, (select distinct student_id from address ) tmp where students.id = tmp.student_id;
+----+-------------+------------+------------+------+---------------+-------------+---------+------------------+------+----------+--------------------------+
| id | select_type | table      | partitions | type | possible_keys | key         | key_len | ref              | rows | filtered | Extra                    |
+----+-------------+------------+------------+------+---------------+-------------+---------+------------------+------+----------+--------------------------+
|  1 | PRIMARY     | students   | NULL       | ALL  | PRIMARY       | NULL        | NULL    | NULL             |    2 |   100.00 | NULL                     |
|  1 | PRIMARY     | <derived2> | NULL       | ref  | <auto_key0>   | <auto_key0> | 4       | test.students.id |    2 |   100.00 | Using where; Using index |
|  2 | DERIVED     | address    | NULL       | ALL  | NULL          | NULL        | NULL    | NULL             |    1 |   100.00 | Using temporary          |
+----+-------------+------------+------------+------+---------------+-------------+---------+------------------+------+----------+--------------------------+
3 rows in set, 1 warning (0.00 sec)

当前查询编使用到了 Using index,Using where, Using temporary,也就是说,我们在使用SQL语句时,可能会使用到多个extra。

示例中SQL源代码

-- ----------------------------
-- Table structure for class
-- ----------------------------
CREATE TABLE `class`  (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `no` int(11) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of class
-- ----------------------------
INSERT INTO `class` VALUES (1, '六年级', 20060);
INSERT INTO `class` VALUES (2, '七年级', 20061);

-- ----------------------------
-- Table structure for students
-- ----------------------------
CREATE TABLE `students`  (
  `id` int(10) UNSIGNED NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `classId` int(11) NULL DEFAULT NULL,
  `no` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE,
  INDEX `fk_classId`(`classId`) USING BTREE,
  CONSTRAINT `fk_classId` FOREIGN KEY (`classId`) REFERENCES `class` (`id`) ON DELETE NO ACTION ON UPDATE NO ACTION
) ENGINE = InnoDB AUTO_INCREMENT = 3 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of students
-- ----------------------------
INSERT INTO `students` VALUES (1, '张三', 1, '2000000221212');
INSERT INTO `students` VALUES (2, '李四', 1, '2000000021212');

CREATE TABLE `address`  (
  `student_id` int(10) NOT NULL,
  `province` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `city` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `county` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `detail` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `id` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 2 CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of address
-- ----------------------------
INSERT INTO `address` VALUES (1, '汉东省', '汉阳市', '经开区', '经开路18号', 1);
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

互联网全栈开发实战

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

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

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

打赏作者

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

抵扣说明:

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

余额充值