一篇文章搞懂MySQL explain

explain是什么?

explain可以模拟MySQL优化器执行SQL语句,可以很好的分析SQL语句或表结构的性能瓶颈。这是网上大多数人给出的相同解释,我在简单使用过后认为,explain是一个帮助我们优化SQL和表结构的工具,更多的时候它都是在帮助我们去了解表结构和一段SQL在执行过程中索引的使用情况,通过这些信息去指导我们设计和使用索引,优化表之间的关系,写出更优的SQL。

expalin具体可以做什么?

explain可以显示表的读取顺序、数据读取操作的操作类型、哪些索引可以使用、哪些索引被实际使用和表之间的引用等,没有看到它的实际使用结果就去谈这些,对第一次接触的小伙伴可能会感觉有点云里雾里,所以我选择将具体功能描述留到后面来介绍。

数据准备

DROP TABLE IF EXISTS `college`;
CREATE TABLE `college`  (
  `c_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT '学院ID',
  `c_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '学院名称',
  `c_address` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '学院位置',
  `c_phone_number` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学院联系电话',
  `status` tinyint(20) NOT NULL DEFAULT 1 COMMENT '状态',
  `c_create_time` datetime(0) NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
  `c_update_time` datetime(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '记录最后更新时间',
  PRIMARY KEY (`c_id`) USING BTREE,
  UNIQUE INDEX `c_name`(`c_name`) USING BTREE COMMENT '学院名称唯一索引'
) ENGINE = InnoDB AUTO_INCREMENT = 146 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '学院表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of college
-- ----------------------------
INSERT INTO `college` VALUES (133, '信息学院', '明义楼', '17728103467', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (134, '美术学院', '弘毅楼', '13131652078', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (135, '机械学院', '锐思楼', '18018347920', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (136, '建筑学院', '明义楼', '17896457138', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (137, '物流学院', '明义楼', '13382501479', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (138, '外国语学院', '致远楼', '15367930248', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (139, '马克思学院', '锐思楼', '15015480796', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (140, '历史学院', '致远楼', '13138271650', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (141, '统计学院', '锐思楼', '18121479508', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (142, '化工学院', '致远楼', '18901467238', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (143, '环境学院', '弘毅楼', '18720698537', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (144, '地理学院', '弘毅楼', '13096074853', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (145, '物理学院', '锐思楼', '13152139876', 1, '2020-07-31 09:30:37', NULL);
INSERT INTO `college` VALUES (146, '教育学院', '致远楼', '18760842719', 1, '2020-07-31 09:30:37', NULL);

SET FOREIGN_KEY_CHECKS = 0;
DROP TABLE IF EXISTS `student`;
CREATE TABLE `student`  (
  `stu_id` bigint(11) NOT NULL AUTO_INCREMENT COMMENT 'ID',
  `c_id` bigint(11) NOT NULL COMMENT '学院ID',
  `stu_name` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '学生姓名',
  `stu_number` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '学号',
  `stu_phone_number` varchar(20) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '手机号码',
  `stu_create_time` datetime(0) NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '记录创建时间',
  `stu_update_time` datetime(0) NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP(0) COMMENT '记录最后修改时间',
  `c_name` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '学院名称',
  `status` tinyint(2) NOT NULL COMMENT '状态',
  PRIMARY KEY (`stu_id`) USING BTREE,
  INDEX `stu_college`(`c_id`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 19 CHARACTER SET = utf8 COLLATE = utf8_general_ci COMMENT = '学生表' ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 0;

INSERT INTO `student`(`stu_id`, `c_id`, `stu_name`, `stu_number`, `stu_phone_number`, `stu_create_time`, `stu_update_time`, `c_name`, `status`) VALUES (19, 146, '胡明', '2016000001', '15313927480', '2020-07-31 22:54:59', NULL, '教育学院', 1);
INSERT INTO `student`(`stu_id`, `c_id`, `stu_name`, `stu_number`, `stu_phone_number`, `stu_create_time`, `stu_update_time`, `c_name`, `status`) VALUES (20, 143, '赵滔', '2020000002', '17849053678', '2020-07-31 22:54:59', NULL, '环境学院', 1);
INSERT INTO `student`(`stu_id`, `c_id`, `stu_name`, `stu_number`, `stu_phone_number`, `stu_create_time`, `stu_update_time`, `c_name`, `status`) VALUES (21, 137, '习利', '2017000003', '15092817650', '2020-07-31 22:54:59', NULL, '物流学院', 1);
INSERT INTO `student`(`stu_id`, `c_id`, `stu_name`, `stu_number`, `stu_phone_number`, `stu_create_time`, `stu_update_time`, `c_name`, `status`) VALUES (22, 135, '戴长城', '2020000004', '15019286704', '2020-07-31 22:54:59', NULL, '机械学院', 1);
INSERT INTO `student`(`stu_id`, `c_id`, `stu_name`, `stu_number`, `stu_phone_number`, `stu_create_time`, `stu_update_time`, `c_name`, `status`) VALUES (23, 140, '王国庆', '2020000005', '13332947658', '2020-07-31 22:54:59', NULL, '历史学院', 1);

使用方法

explain+SQL 例如:

EXPLAIN SELECT c_name FROM college WHERE c_name = '美术学院';

执行结果:
在这里插入图片描述
结果概述:

列名 描述
id 查询序列号,用于标识执行顺序
select_type 操作类型,用于区分各种复杂查询
table 表名称
partitions 匹配的分区
type 访问类型
possible_keys 该表中可以使用的索引,但不一定使用
key 本次操作实际用到的索引
key_len 索引中使用的字节数
ref 显示关联字段或者指定常量来选择需要的记录
rows 执行查询的行数
filtered 查询的表记录数占总记录数比例
Extra 其他重要信息

实际操作与使用分析

id

​ id是查询的序列号,用于判断查询的执行顺序,id的排序共有两种情况,分别是id值相同,另一种就是值不相同。

  1. id值相同

    EXPLAIN SELECT c.c_name FROM college c,student s WHERE c.c_name = '美术学院';
    

    结果:
    在这里插入图片描述
    这种情况就是id相同,判断他们执行顺序的方法就是根据table列的顺序,由上至下依次执行,所以执行顺序应该为c -> s

  2. id不同

    EXPLAIN SELECT c.c_name FROM college c WHERE c.c_name = (select c_name from student WHERE stu_id = 18) ;
    

    结果:
    在这里插入图片描述
    这种情况就是id不相同,判断他们执行顺序的方法就是根据id值,值越大优先级越高越先执行,所以执行顺序应该为student -> c

  3. 相同与不相同都存在

    其实这并不是一种特殊情况,只是介绍两种情况混合时如何排序

    EXPLAIN SELECT c.c_name FROM college c,(select c_name from student s1) s WHERE c.c_name = (select c_name from student s2 WHERE stu_id = 205466109) ;
    

    结果:
    在这里插入图片描述
    首先分析相同的,顺序为c -> s1,然后分析不相同的s2 ->c并且s2 -> s1,所以执行顺序为s2 -> c -> s1。也可以先分析不相同的。

select_type

​ 查询的类型,主要用于区别普通查询、联合查询、子查询等复杂的查询。主要有六种查询类型。

  1. SIMPLE
    简单的select查询,不包含复杂查询。
    EXPLAIN SELECT c_name FROM college WHERE c_name = '美术学院';
    
    结果:
    在这里插入图片描述
    这个查询就是普通的检查查询语句。
  2. PRIMARY
    在包含子查询的SQL语句中,最外层的查询其select_type为PRIMARY
    EXPLAIN SELECT c.c_name FROM college c WHERE c.c_name = (select c_name from student WHERE stu_id = 19) ;
    
    结果:
    在这里插入图片描述
  3. SUBQUERY
    在select或者where语句中包含的子查询
    EXPLAIN SELECT c.c_name,(select c_name from student s1 where c_name = "美术学院") FROM college c WHERE c.c_name = (select c_name from student s2 WHERE stu_id = 19) ;
    
    结果:
    在这里插入图片描述
  4. DERIVED(目前有实验结果问题,可能是理解有误
    在from中生成临时表的子查询
    EXPLAIN SELECT * FROM (select * from student) t where stu_id = 19
    
    结果:
    在这里插入图片描述
  5. UNION
    如果第二个select出现在union之后,则被标记为UNION
    EXPLAIN SELECT * FROM college c1 union SELECT * FROM college c2
    
    结果:在这里插入图片描述
  6. UNION RESULT
    从union表获取结果的select
    EXPLAIN SELECT * FROM college c1 union SELECT * FROM college c2
    
    结果:
    在这里插入图片描述
table

显示操作的那张表

EXPLAIN SELECT c.c_name FROM college c,student s WHERE c.c_name = '美术学院';

结果:
在这里插入图片描述
序号为1和2的两个查询操作的表分别为c、s

partitions

匹配的分析,不是太理解。

type

连接类型

  1. system
    表只有一行记录(等于系统表),是const的特例类型,平时不会出现,可以忽略不计。
  2. const
    表示通过一次索引就找到了结果,常出现于primary key或unique索引。因为只匹配一行数据,所以查询非常快。如将主键置于where条件中,MySQL就能将查询转换为一个常量。
    explain SELECT stu_name FROM student where stu_number = '20170000795';
    

在这里插入图片描述
3. eq_ref
唯一索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见主键或唯一索引扫描,注意这里是扫描而不是匹配

explain SELECT s.stu_id FROM student s left JOIN college c on s.c_id = c.c_id;

在这里插入图片描述
4. ref
非唯一性索引扫描,返回匹配某个单独值的所有行。本质上也是一种索引访问,返回匹配某值(某条件)的多行值,属于查找和扫描的混合体。

explain SELECT stu_id FROM student where c_id = 12;

在这里插入图片描述
5. range
只检索给定范围的行,例如使用between、in等关键字限定范围。

explain SELECT stu_id FROM student where c_id BETWEEN 12 and 17;

在这里插入图片描述
6. index
只遍历索引树,即select后输出字段均为索引,其他地方不涉及索引的扫描与匹配。

explain SELECT stu_id FROM student;

在这里插入图片描述
7. ALL
全表扫描,这个较容易理解

explain SELECT * FROM student;

在这里插入图片描述
8. index_merge
索引合并

explain SELECT stu_name FROM student where stu_number='20170000795' or c_id = 135;

在这里插入图片描述

possible_keys

可能应用在这张表的索引,一个或者多个,只是可能被应用。

key

实际使用到的索引。

key_len

表示索引中所使用的字节数,可通过该列计算查询中使用的索引长度,标识索引字段的最大可能长度。

ref

查询的约束条件或者连接查询的关联字段,如果条件为常量则值为const,如果是关联查询则显示关联字段。

  1. 常量
    EXPLAIN SELECT * FROM college where c_name = "美术学院";
    

在这里插入图片描述
2. 字段

EXPLAIN SELECT * FROM college c LEFT JOIN student s on c.c_id = s.c_id;

在这里插入图片描述

rows

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

filtered

查询的表记录数占总记录数比例

Extra

额外信息

  1. Using filesort
    说明MySQL会对数据使用一个外部的索引排序,而不是按照表内的索引顺序进行读取, 如果表中数据量非常大的话就需要建立索引来解决。
    explain SELECT stu_number FROM student ORDER BY stu_number;
    

在这里插入图片描述
2. Using temporary
使用了临时表保存中间结果,常见于排序order by和分组查询group by。

explain SELECT stu_number FROM student ORDER BY stu_number;

在这里插入图片描述
3. Using index
表明相应的select操作中使用了覆盖索引,避免访问表的额外数据行,效率不错。如果同时出现了Using where,表明索引被用来执行索引键值的查找。如果没有同时出现Using where,表明索引用来读取数据而非执行查找动作。
覆盖索引:select的数据列只用从索引中就能够取得,不必读取数据行,MySQL可以利用索引返回select列表中的字段,而不必根据索引再次读取数据文件,即查询列要被所建的索引覆盖
现在给student表的stu_number添加索引

explain SELECT stu_number FROM student GROUP  BY stu_number;

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/qq_41345281/article/details/107730646