Mysql索引的基本知识-explain的使用

一、索引的优点

1、能大大减少服务器需要扫描的数据量。

2、帮助服务器避免排序和临时表。

3、将随机io变成顺序io(顺序 I/O : 物理上读取连续的的磁盘空间上的数据;随机 I/O : 非连续的磁盘空间上的数据;MySQL中数据是存储在磁盘上的,如果使用的是innodb执行引擎,索引的结构为 B+树 , 而B+树的数据全部放在叶子节点,所以数据存储是连续的,在查询的时候走的是顺序io,这样大大减少了寻址的时间,效率上提升很大)。

二、explain关键字的使用

1、前提条件创建三张表user 、role 、role_user;

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
 `id` int(11) NOT NULL,
 `name` varchar(25) DEFAULT NULL,
 `age` int(11)  NOT NULL DEFAULT 0,
 `update_time` datetime DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `user` (`id`, `name`, `age`,`update_time`) VALUES (1,'张三',23,'2020-12-22 15:27:18'), (2,'李四',24,'2020-06-21 15:27:18'), (3,'王五',25,'2020-07-20 15:27:18');

DROP TABLE IF EXISTS `role`;
CREATE TABLE `role` (
 `id` int(11) NOT NULL AUTO_INCREMENT,
 `name` varchar(10) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `index_name` (`name`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `role` (`id`, `name`) VALUES (1,'产品经理'),(2,'技术经理'),(3,'项目总监');

DROP TABLE IF EXISTS `role_user`;
CREATE TABLE `role_user` (
 `id` int(11) NOT NULL,
 `role_id` int(11) NOT NULL,
 `user_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `index_role_user_id` (`role_id`,`user_id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

INSERT INTO `role_user` (`id`, `role_id`, `user_id`) VALUES (1,2,1),(2,1,2),(3,3,3); 

此时user表中有三条数据:

2、索引创建前使用explain关键字:

3、创建普通索引:

 4、索引创建后使用explain关键字:

 通过以上索引创建前后对explain的使用可以看出这里对应的12参数进行了相应的变化,explain关键字是Mysql中sql优化的常用「关键字」,通常都会使用explain来「查看sql的执行计划,而不用执行sql」,从而快速的找出sql的问题所在;能看懂这个执行计划,你离精通sql优化就不远了,下面就来详细的介绍这12个字段分别表示什么意思;

 

三、如何查看查询计划

解析对应12个参数:

  • id
  • select_type
  • table
  • partitions
  • type
  • possible_keys
  • key
  • key_len
  • ref
  • rows
  • filtered
  • Extra

1、第一个参数id(表示执行的顺序  例如在子查询的时候会出现顺序问题):

 explain select a.id ,a.name ,a.age ,b.name as role_name from user a,role b ,role_user c where a.id = c.user_id and b.id = c.role_id;

explain select * from role_user where user_id =(select id from user where name = '李四') and role_id =(select id from role where name = '产品经理');

【总结】id 相同执行顺序由上而下;id不同  id越大优先级越高:3>2>1;

2、select_type(分别用来表示查询的类型,主要是用于区别普通查询、联合查询、子查询等的复杂查询)

select_type查询类型说明
SIMPLE 简单的select查询,查询中不包含子查询或者是union查询;
PRIMARY 查询中包含子查询,最外层的查询会被标记为PRIMARY,或者是包含union查询 union关键字之前的查询会标记为PRIMARY,也就是最后执行的语句;
SUBQUERY 查询中包含子查询,除最外层查询以外,内层作为条件的查询会被标记为SUBQUERY;
DEPENDENT SUBQUERY  子查询中的第一个 select 查询,依赖于外部 查询的结果集;
DERIVED 在FROM列表中包含的子查询被标记为DERIVED(衍生),MySQL会递归执行这些子查询,把结果放在临时表中;
UNOIN 若第二个SELECT出现在UNION之后,则被标记为UNION:若UNION包含在FROM子句的子查询中,外层SELECT将被标记为:DERIVED;
DEPENDENT UNION UNION 中的第二个或随后的 select 查询,依 赖于外部查询的结果集;
UNIONRESULT 从UNION表获取结果的SELECT;

3、table :实际查询的表;

4、partitions

5、type

一般type有一下几种:

  • system
  • const 
  • eq_ref
  • ref
  • range 
  • index 
  • all
system > const > eq_ref > ref > range > index > all

一般来说,得保证查询至少达到range级别,最好能达到ref。

system 表只有一行记录(等于系统表),这是const类型的特列,平时不会出现,这个也可以忽略不计

const 表示通过索引一次就找到了,const用于比较primary key 或者unique索引。因为只匹配一行数据,所以很快。如将主键置于where列表中,MySQL就能将该查询转换为一个常量。

eq_ref 唯一性索引扫描,对于每个索引键,表中只有一条记录与之匹配。常见于主键或唯一索引扫描

ref 非唯一性索引扫描,返回匹配某个单独值的所有行,本质上也是一种索引访问,它返回所有匹配某个单独值的行,然而,它可能会找到多个符合条件的行,所以他应该属于查找和扫描的混合体。

range 只检索给定范围的行,使用一个索引来选择行,key列显示使用了哪个索引,一般就是在你的where语句中出现between、< 、>、in等的查询,这种范围扫描索引比全表扫描要好,因为它只需要开始于索引的某一点,而结束于另一点,不用扫描全部索引。

index Full Index Scan,Index与All区别为index类型只遍历索引树。这通常比ALL快,因为索引文件通常比数据文件小。(也就是说虽然all和Index都是读全表,但index是从索引中读取的,而all是从硬盘读取的)。

all Full Table Scan 将遍历全表以找到匹配的行。

6、possible_keys

possible_keys 显示可能应用在这张表中的索引,一个或多个。查询涉及到的字段上若存在索引,则该索引将被列出,但不一定被查询实际使用。

7、key

实际使用的索引,如果为NULL,则没有使用索引。(可能原因包括没有建立索引或索引失效)。

8、key_len

表示索引中使用的字节数,可通过该列计算查询中使用的索引的长度,在不损失精确性的情况下,长度越短越好。key_len显示的值为索引字段的最大可能长度,并非实际使用长度,即key_len是根据表定义计算而得,不是通过表内检索出的。

9、ref

显示索引的那一列被使用了,如果可能的话,最好是一个常数。哪些列或常量被用于查找索引列上的值。

10、rows

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

11、filtered

12、Extra(额外的信息说明)

12.1、Impossible HAVING

HAVING子句总是为false,不能选择任何行

12.2、Impossible WHERE

WHERE子句始终为false,不能选择任何行

12.3、Impossible WHERE noticed after reading const tables:MySQL读取了所有的const和system表,并注意到WHERE子句总是为false

12.4、No matching min/max row

没有满足SELECT MIN(…)FROM … WHERE查询条件的行

12.5、no matching row in const table

表为空或者表中根据唯一键查询时没有匹配的行

12.6、No tables used

没有FROM子句或者使用DUAL虚拟表

12.7 、Select tables optimized away

当我们使用某些聚合函数来访问存在索引的某个字段时,优化器会通过索引直接一次定位到所需要的数据行完成整个查询。在使用某些聚合函数如min, max的query,直接访问存储结构(B树或者B+树)的最左侧叶子节点或者最右侧叶子节点即可,这些可以通过index解决。

12.8、Using filesort
当Query 中包含 ORDER BY 操作,而且无法利用索引完成排序操作的时候,MySQL Query Optimizer 不得不选择相应的排序算法来实现。数据较少时从内存排序,否则从磁盘排序。Explain不会显示的告诉客户端用哪种排序。官方解释:“MySQL需要额外的一次传递,以找出如何按排序顺序检索行。通过根据联接类型浏览所有行并为所有匹配WHERE子句的行保存排序关键字和行的指针来完成排序。然后关键字被排序,并按排序顺序检索行

12.9、Using index

仅使用索引树中的信息从表中检索列信息,而不需要进行附加搜索来读取实际行(使用二级覆盖索引即可获取数据)。 当查询仅使用作为单个索引的一部分的列时,可以使用此策略。示例中第一个查询所有数据时,无法通过age的覆盖索引来获取整行数据,所以需要根据主键id回表查询表数据。

12.10、Using where

表示Mysql将对 存储引擎 (storage engine)提取的结果进行过滤,过滤条件字段(update_time)无索引;

12.11、Using index condition

Using index condition 会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行;

12.12、Using index for group-by

数据访问和 Using index 一样,所需数据只须要读取索引,当Query 中使用GROUP BY或DISTINCT 子句时,如果分组字段也在索引中,Extra中的信息就会是 Using index for group-by。注:和Using index一样,只需读取覆盖索引

12.13、Using join buffer 

12.14、Using temporary

要解决查询,MySQL需要创建一个临时表来保存结果。 如果查询包含不同列的GROUP BY和ORDER BY子句,则通常会发生这种情况。官方解释:”为了解决查询,MySQL需要创建一个临时表来容纳结果。典型情况如查询包含可以按不同情况列出列的GROUP BY和ORDER BY子句时。很明显就是通过where条件一次性检索出来的结果集太大了,内存放不下了,只能通过家里临时表来辅助处理;

 

四、索引的用处

1、user表对应的索引(index):

 2、索引的用处有一下几种:

 

2.1、能够快速匹配where条件对应的数据行。

这里age字段建立了索引,update_time 没有建立索引,从一下查询计划可以看出没有索引的查询进行了全表扫描,而有索引的查询可以缩小扫描的数据条数;

例1:explain select * from user where update_time >='2020-07-20 15:27:18';

例2:explain select * from user where age > 24;

2.2、从consideration中消除行,如果可以在多个索引之间进行选择,mysql通常会使用找到最少行的索引

例1:explain select * from user where age = 25 and name like '王%';

例2:explain select * from user where age >23 and name like '王%';

由上图可以看出如果建立了多个索引 mysql优化器会在这些索引中选择查询最少行的索引。如果数据库表具有多列索引,数据库优化器可以使用任何列的索引最左前缀来查找行。

2.3、如果表具有多列索引,则优化器可以使用索引的任何最左前缀来查找行

最左前缀匹配,当查询条件精确匹配索引的左边连续一个或几个列时,索引可以被用到,但是只能用到一部分,即条件所组成的最左前缀。

在mysql中执行查询时,只能使用一个索引,如果我们在lname,fname,age上分别建索引,执行查询时,只能使用一个索引,mysql会选择一个最严格(获得结果集记录数最少)的索引。

下面用同一个sql语句,图一使用的索引,图二去掉了索引,显然第一个走了索引,因为在innodb myisam 两种执行引擎下索引的结构都为b+tree,因此可以直接在有序的索引结构中获取最大值和最小值所对应的主键id,然后进行回表,查找到对应的值。

注:只有主键索引的叶子节点中才会存储行数据,普通索引在叶子节点中值存储对应的主键值;

 

                                    图1:gender建立了对应的索引 

 

                                    图2:gender去除了对应的索引

    • 如果排序或分组时在可用索引的最左前缀上完成的,则对表进行排序和分组

 

    • 在某些特定的情况下,是不需要查询整行数据来检索值。

当查询的列为主键id时,只需要通过普通索引进行查询就可以,因为普通索引的叶子节点存储的为主键id,这时不需要进行回表;因为已经检索到需要的主键列,这就是所谓的覆盖索引。

 

    • 当有多个索引列可以用来检索时  mysql优化器会自动选择最短的长度的索引来检索行。

在表中建立了以下的索引  其中绿色为组合索引:

 

其中id为int    age 和 score为smallint  mysql优化器会自动选择最短的索引进行检索;

 

当把age的数据类型改成bigint后   mysql选择了score;

 

posted @ 2020-05-29 15:42  AmourLee  阅读(854)  评论(0编辑  收藏  举报