小白学习mysql之优化基础(EXPLAIN的链接类型)

导语:

不少状况下,有不少人用各类select语句查询到了他们想要的数据后,每每便觉得工做圆满结束了。
这些事情每每发生在一些学生亦或刚入职场但以前又没有很好数据库基础的小白身上,但所谓闻道有前后,只要咱们小白好好学习,每天向上,仍是很靠谱的。mysql

当一个sql查询语句被写出来以后,其实你的工做只完成了一小半,接下来更重要的工做是评估你本身写的sql的质量与效率。mysql为咱们提供了颇有用的辅助武器explain,它向咱们展现了mysql接收到一条sql语句的执行计划。根据explain返回的结果咱们即可以知道咱们的sql写的怎么样,是否会形成查询瓶颈,同时根据结果不断的修改调整查询语句,从而完成sql优化的过程。sql

虽然 explain返回的结果项不少,这里咱们只关注三种,分别是type,key,rows。其中key代表的是此次查找中所用到的索引,rows是指此次查找数据所扫描的行数(这里能够先这样理解,但其实是内循环的次数)。而type则是本文要详细记录的链接类型,前两项重要并且简单,无需多说。数据库

type -- 链接类型

type意味着类型,这里的type官方全称是“join type”,意思是“链接类型”,这样很容易给人一种错觉以为必须须要俩个表以上才有链接类型。事实上这里的链接类型并不是字面那样的狭隘,它更确切的说是一种数据库引擎查找表的一种方式,在《高性能mysql》一书中做者更是以为称呼它为访问类型更贴切一些。mysql优化

mysql5.7中type的类型达到了14种之多,这里只记录和理解最重要且常常碰见的六种类型,它们分别是all,index,range,ref,eq_ref,const。从左到右,它们的效率依次是加强的。撇开sql的具体应用环境以及其余因素,你应当尽可能优化你的sql语句,使它的type尽可能靠右,但实际运用中仍是要综合考虑各个方面的。性能

接下来,为了演示和重现这几种链接类型,我新建了一个数据测试表,以方面更好的理解这五种类型。学习

| employee | CREATE TABLE `employee` (
  `rec_id` int(11) NOT NULL AUTO_INCREMENT,
  `no` varchar(10) NOT NULL,
  `name` varchar(20) NOT NULL,
  `position` varchar(20) NOT NULL,
  `age` varchar(2) NOT NULL,
  PRIMARY KEY (`rec_id`)
) ENGINE=InnoDB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8 |

all

这即是所谓的“全表扫描”,若是是展现一个数据表中的所有数据项,却是以为也没什么,若是是在一个查找数据项的sql中出现了all类型,那一般意味着你的sql语句处于一种最原生的状态,有很大的优化空间。
为何这么说呢?由于all是一种很是暴力和原始的查找方法,很是的耗时并且低效。用all去查找数据就比如这样的一个情形:S学校有俩万人,我告诉你你给我找到小明,而后你怎么作呢!你固然是把全校俩万人挨个找一遍,即便你很幸运第一我的便找到了小明,可是你仍然不能停下,由于你没法确认是否有另一个小明存在,直到你把俩万人找完为止。因此,基本全部状况,咱们都要避免这样类型的查找,除非你不得不这样作。
以employee表为例,下面一种情形即是all类型的查找:测试

mysql> explain select * from employee where `no` = '20150001';
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra       |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+
|  1 | SIMPLE      | employee | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using where |
+----+-------------+----------+------+---------------+------+---------+------+------+-------------+

这是由于no列既不是主键也不是索引,所以只能采用全表扫描来查找目标no。优化

index

这种链接类型只是另一种形式的全表扫描,只不过它的扫描顺序是按照索引的顺序。这种扫描根据索引而后回表取数据,和all相比,他们都是取得了全表的数据,并且index要先读索引并且要回表随机取数据,所以index不可能会比all快(取同一个表数据),但为何官方的手册将它的效率说的比all好,惟一可能的缘由在于,按照索引扫描全表的数据是有序的。这样一来,结果不一样,也就无法比效率的问题了。
若是必定要比效率,只须要获取这个表的数据而且排序即可以看出来谁比谁效率高了:操作系统

mysql> explain select * from employee order by `no` ;
+----+-------------+----------+------+---------------+------+---------+------+------+----------------+
| id | select_type | table    | type | possible_keys | key  | key_len | ref  | rows | Extra          |
+----+-------------+----------+------+---------------+------+---------+------+------+----------------+
|  1 | SIMPLE      | employee | ALL  | NULL          | NULL | NULL    | NULL |    5 | Using filesort |
+----+-------------+----------+------+---------------+------+---------+------+------+----------------+
mysql> explain select * from employee order by rec_id ;
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------+
|  1 | SIMPLE      | employee | index | NULL          | PRIMARY | 4       | NULL |    5 | NULL  |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------+

上面能够看出,根据no列排序的链接类型是all型的,可是注意extra列是用到了排序(Using filesort),而根据rec_id列排序的链接类型是index,并且获得的结果天然是有序的,不准额外的排序。可能正是由于这个缘故,index的效率比all高,但注意这须要相同的条件才成立(既须要排序)。code

若是链接类型为type,并且extra列中的值为‘Using index’,那么称这种状况为 索引覆盖
索引覆盖意味着什么呢?想象这样一种场景,若是说一本新华字典是一张表,固然前面的索引部分(假设按照部首的索引)是这张表的索引,那么索引覆盖就至关于根据部首索引获取第一个字到最后一个字(新华字典的全部字)。咱们得到了字典中全部的字,然而咱们并无查一次表,由于咱们想要的都早索引中,即索引覆盖。

mysql> explain select rec_id from employee ;
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
| id | select_type | table    | type  | possible_keys | key     | key_len | ref  | rows | Extra       |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+
|  1 | SIMPLE      | employee | index | NULL          | PRIMARY | 4       | NULL |    5 | Using index |
+----+-------------+----------+-------+---------------+---------+---------+------+------+-------------+

上例获取的rec_id恰好为索引列,所以无需回表取数据。

range

range指的是有范围的索引扫描,相对于index的全索引扫描,它有范围限制,所以要优于index。关于range比较容易理解,须要记住的是出现了range,则必定是基于索引的。同时除了显而易见的between,and以及'>','<'外,in和or也是索引范围扫描。

ref

出现该链接类型的条件是: 查找条件列使用了索引并且不为主键和unique。其实,意思就是虽然使用了索引,但该索引列的值并不惟一,有重复。这样即便使用索引快速查找到了第一条数据,仍然不能中止,要进行目标值附近的小范围扫描。但它的好处是它并不须要扫全表,由于索引是有序的,即使有重复值,也是在一个很是小的范围内扫描。下面为了演示这种情形,给employee表中的name列添加一个普通的key(值容许重复)

alter table employee add key I_EMPLOYEE_NAME(`name`);

接下来,在employee表中根据name查找数据的时候,mysql优化器便选择了ref的链接类型。

mysql> explain select * from employee where `name` = '张三';
+----+-------------+----------+------+----------------+----------------+---------+-------+------+-----------------------+
| id | select_type | table    | type | possible_keys  | key            | key_len | ref   | rows | Extra                 |
+----+-------------+----------+------+----------------+----------------+---------+-------+------+-----------------------+
|  1 | SIMPLE      | employee | ref  | I_EMPLOYEE_NAM | I_EMPLOYEE_NAM | 62      | const |    1 | Using index condition |
+----+-------------+----------+------+----------------+----------------+---------+-------+------+-----------------------+

ref_eq

ref_eq 与 ref相比牛的地方是,它知道这种类型的查找结果集只有一个?什么状况下结果集只有一个呢!那即是使用了主键或者惟一性索引进行查找的状况,好比根据学号查找某一学校的一名同窗,在没有查找前咱们就知道结果必定只有一个,因此当咱们首次查找到这个学号,便当即中止了查询。这种链接类型每次都进行着精确查询,无需过多的扫描,所以查找效率更高,固然列的惟一性是须要根据实际状况决定的。
在单个表中,曾尝试了不少方法想出现ref_eq的链接类型,然而不少时候出现的都是const,所以不得不随手链接了一张表获得了想要的链接类型,该表的建表代买为。(博主比较懒,链接了两个没有关系的表,o(╯□╰)o)

CREATE TABLE `score` (
  `rec_id` INT(11) NOT NULL AUTO_INCREMENT,
  `stu_id` INT(11) NOT NULL,
  `mark` INT(11) NOT NULL DEFAULT '0',
  PRIMARY KEY (`rec_id`),
  UNIQUE KEY `UK_SCORE_STU_ID` (`stu_id`)
) ENGINE=INNODB AUTO_INCREMENT=6 DEFAULT CHARSET=utf8

employee表中有五条数据,score表中有对应的五条数据,其中employee的rec_id 和score的stu_id 是一一对应的。

mysql> explain select ep.name,sc.mark from employee ep,score sc where ep.rec_id = sc.stu_id;
+----+-------------+-------+--------+-----------------+---------+---------+-----------------+------+-------+
| id | select_type | table | type   | possible_keys   | key     | key_len | ref             | rows | Extra |
+----+-------------+-------+--------+-----------------+---------+---------+-----------------+------+-------+
|  1 | SIMPLE      | sc    | ALL    | UK_SCORE_STU_ID | NULL    | NULL    | NULL            |    5 | NULL  |
|  1 | SIMPLE      | ep    | eq_ref | PRIMARY         | PRIMARY | 4       | my_db.sc.stu_id |    1 | NULL  |
+----+-------------+-------+--------+-----------------+---------+---------+-----------------+------+-------+

上面就能够看到score表是全表扫描的类型,rows=5表明外层表循环了五次(由于有五条数据),可是employee表的rows怎么是1,怎么可能?刚开始也是很疑惑,这与mysql的查询原理息息相关,rows实际反映的是查询的内循环数,针对外层的每一条数据匹配,employee的确一枪就能够命中,所以rows为1。

const

一般状况下,若是将一个主键放置到where后面做为条件查询,mysql优化器就能把此次查询优化转化为一个常量。至于如何转化以及什么时候转化,这个取决于优化器。

总结

explain 就像一面镜子,有事没事写完sql记得explain一下。同时,在写文章也发现,有不少东西和细节,想要明白清楚,也是没有那么简单的,须要对操做系统以及数据库的底层查询和运行原理要有一个清楚的理解。同时type的几种类型几乎都是基于索引之上的,所以须要对索引有个深刻的了解,并且explain的结果能够指导咱们何时加索引,何时不加索引,从而让咱们更好的使用索引。

explain

相关文章
相关标签/搜索