前言
有时候,我们写好
SQL
后发现执行查询的时间超过了预期,但自己已经有根据表的已有索引去写SQL
语句,这个时候就要考虑是否是在查询的过程中出现了索引失效的问题。那么我们可以怎么样去验证SQL
在执行过程中是否有使用到索引呢?数据库给我们提供了explain
关键字去查看sql的执行过程。
本篇文章将介绍explain
关键字的用法和各个参数的含义,以期对该函数不了解的开发人员能够快速掌握SQL
分析的技能。
本篇文章将以department
和employee
两张表(具体见后面的表结构参考)为例,进行文章的讲解,其中employee
的name
和age
做了组合索引 。
语法
explain 要分析的SQL语句
以上图为例,当我们使用explain
来分析select * from employee这条句子后发现,出现了id
、select_type
、table
、type
、possible_keys
、key
、key_len
、ref
、rows
、Extra
共十个字段。那么这些字段分别是什么意思呢?我们可以通过这些字段的值来判断当前索引是否失效呢?下面,将对上述问题一一进行解答。
(一)explain字段含义解析
1. id
id表示执行select子句或者操作表的顺序
- id相同时,从上到下依次执行
- id不同时,根据id从大到小依次执行。(如果是子查询,id的序号会递增,id值越大优先级越高,越先被执行)
以上图为例,使用explain
关键字查看sql执行过程后发现,会先执行id
为2
的子查询语句,再分别查询emp
和dep
两张表。至于为什么有这样的先后之分,是因为sql执行查询的时候会优先执行子查询语句的内容。
2. select_type
表示每个查询项的查询类型
类型 | 作用 |
---|---|
SIMPLE | 简单SELECT,不使用UNION或子查询等 |
PRIMARY | 查询中若包含任何复杂的子部分,最外层的select被标记为PRIMARY |
UNION | UNION中的第二个或后面的SELECT语句 |
DEPENDENT UNION | UNION中的第二个或后面的SELECT语句,取决于外面的查询 |
UNION RESULT | UNION中的第二个或后面的SELECT语句,取决于外面的查询 |
SUBQUERY | 子查询中的第一个SELECT |
DERIVED | 派生表的SELECT, FROM子句的子查询 |
mysql> explain select * from department where id=(select id from employee where name='tony') union select * from department where id=(select id from employee where name='niky');
+------+---------------+-----------+-------+--------------+----------+---------+-------+-------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+------+---------------+-----------+-------+--------------+----------+---------+-------+-------+-------------+
| 1 | PRIMARY | department | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 2 | SUBQUERY | employee | ALL | NULL | NULL | NULL | NULL | 8 | Using where |
| 3 | UNION | department | const | PRIMARY | PRIMARY | 4 | const | 1 | |
| 4 | SUBQUERY | employee | ALL | NULL | NULL | NULL | NULL | 8 | Using where |
| NULL | UNION RESULT | | ALL | NULL | NULL | NULL | NULL | NULL | |
+------+---------------+-----------+-------+--------------+----------+---------+-------+-------+-------------+
5 rows in set (0.08 sec)
以上面的例子,我们基本上可以看出常见的几种查询类型是在什么情况下使用的。
3. table
显示这一行的数据是关于哪张表的,有时是真实的表名字,有时也可能是以下几种结果
: 指id为M,N行结果的并集 : 该行是指id值为n的行的派生表结果。派生表可能来自例如from子句中的子查询。 : 该行是指id值为n的行的物化子查询的结果。
4. type(重要参数)
连接使用了哪种类别,有无使用索引,常用的类型有:system
, const
, eq_ref
, ref
, range
, index
, ALL
(从左到右,性能越来越差)
类型 | 说明 |
---|---|
all(Full Table Scan) | 没有用到索引,MySQL将遍历全表以找到匹配的行 |
index(Full Index Scan) | index与ALL区别为index类型只遍历索引树 |
range | 只检索给定范围的行,使用一个索引来选择行 |
ref | 表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值 |
eq_ref | 类似ref,区别就在使用的索引是唯一索引,对于每个索引键值,表中只有一条记录匹配,简单来说,就是多表连接中使用primary key或者 unique key作为关联条件 |
const | 该表最多具有一个匹配行,该行在查询开始时读取。 |
system | 该表只有一行(=系统表)。这是[const ]联接类型的特例 。 |
一般来说,sql的级别能够达到eq_ref
~index
的话,都算是性能尚可的查询。
5. possible_keys
表示SQL能够使用哪些索引在表中查询数据,查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询使用。也就是说,这个字段只是列出了可能使用到的索引,当出现理论上有使用索引,实际上没有的情况,很大可能就是出现了索引失效的问题了。
6. key(重要)
key
列显示数据库实际决定使用的键(索引)
如果实际执行查询过程中,没有使用索引,此处的值为NULL。
使用的索引的长度。在不损失精确性的情况下,长度越短越好
7.ref
表示上述表的连接匹配条件,即哪些列或常量被用于查找索引列上的值
这里要注意和type中的ref属性作区分,这里的ref是指作为匹配条件的值,而后者是指查询的一种类型。
如上图,ref对应的值为const,说明作为匹配条件的“niky”是一个常量。
8. rows
表示MySQL根据表统计信息及索引选用情况,估算的找到所需的记录所需要读取的行数。
这里表示需要扫描读取8行数据
9.Extra(重要)
该列包含MySQL解决查询的详细信息,有以下几种情况
-
Using where
使用了WHERE从句来限制哪些行将与下一张表匹配或者是返回给用户。如果不想返回表中的全部行,并且连接类型ALL或index,这就会发生,或者是查询有问题
-
Uing index
列数据是从仅仅使用了索引中的信息而没有读取实际的行动的表返回的,这发生在对表的全部的请求列都是同一个索引的部分的时候,表示mysql服务器将在存储引擎检索行后再进行过滤
Using join buffer
改值强调了在获取连接条件时没有使用索引,并且需要连接缓冲区来存储中间结果。如果出现了这个值,那应该注意,根据查询的具体情况可能需要添加索引来改进能。-
Impossible where
这个值强调了where语句会导致没有符合条件的行。
Using temporary
表示MySQL需要使用临时表来存储结果集,常见于排序和分组查询。-
Using filesort
MySQL中无法利用索引完成的排序操作称为“文件排序”
当出现Using temporary
和Using filesort
的时候,说明这条sql的效率比较低,这个时候就需要优化一下sql或者索引结构了。
(二)使用explain的综合分析小案例
SQL:select emp.age,dep.d_name from employee emp left join department dep on emp.dep_id=dep.id where emp.name='niky' order by dep.id ;
通过explain我们可以看到id列都为1,所以执行顺序将从上到下依次执行;没有使用子查询或者其他复杂的连接,所以此处的select_type
为simple
;查询使用的连接类别为ref
和eq_ref
,说明使用到了索引,查询的性能尚可;possible_key
和key
保持一致,没有出现索引失效的情况;ref
列的值说明了两段查询中使用到了匹配值分别为常量和emp
表的dep_id
字段;Extra
列中显示该sql语句中使用到了临时表和文件内排序,原因是两表关联后根据dep表的id字段排序,没有使用到索引。
这里的话,我们可以改用排序条件来优化sql。
文章参考:
了解MySQL中EXPLAIN解释命令: https://segmentfault.com/a/1190000018729502
MySQL Explain详解:https://www.cnblogs.com/leeego-123/p/11846613.html
相关建表sql参考
-- ----------------------------
-- Table structure for department
-- ----------------------------
DROP TABLE IF EXISTS `department`;
CREATE TABLE `department` (
`id` int(11) NOT NULL,
`d_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL,
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of department
-- ----------------------------
INSERT INTO `department` VALUES (1, 'HR');
INSERT INTO `department` VALUES (2, 'FI');
INSERT INTO `department` VALUES (3, 'RD');
INSERT INTO `department` VALUES (4, 'SD');
INSERT INTO `department` VALUES (5, 'MAR');
-- ----------------------------
-- Table structure for employee
-- ----------------------------
DROP TABLE IF EXISTS `employee`;
CREATE TABLE `employee` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '员工id',
`name` varchar(255) CHARACTER SET utf8 COLLATE utf8_unicode_ci NULL DEFAULT NULL COMMENT '员工姓名',
`dep_id` int(11) NULL DEFAULT NULL COMMENT '所属部门id',
PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 9 CHARACTER SET = utf8 COLLATE = utf8_unicode_ci ROW_FORMAT = Compact;
-- ----------------------------
-- Records of employee
-- ----------------------------
INSERT INTO `employee` VALUES (1, 'amy', 1);
INSERT INTO `employee` VALUES (2, 'tony', 1);
INSERT INTO `employee` VALUES (3, 'jack', 4);
INSERT INTO `employee` VALUES (4, 'jony', 2);
INSERT INTO `employee` VALUES (5, 'niky', 3);
INSERT INTO `employee` VALUES (6, 'tini', 5);
INSERT INTO `employee` VALUES (7, 'itern', 10);
INSERT INTO `employee` VALUES (8, 'bob', 5);