MySQL索引3——Explain关键字和索引使用规则(SQL提示、索引失效、最左前缀法则)

目录

Explain关键字 索引性能分析

Id ——select的查询序列号

Select_type——select查询的类型

Table——表名称

Type——select的连接类型

Possible_key ——显示可能应用在这张表的索引

Key——实际用到的索引

Key_len——实际索引使用到的字节数

Ref    ——索引命中的列或常量

Rows——预计select语句要检查的行数

扫描二维码关注公众号,回复: 16102451 查看本文章

Filtered——返回结果的行数占读取行数的百分比

Extra——显示额外的信息

索引的使用规则

SQL提示

最左前缀法则

索引失效情况

索引的设计原则


Explain关键字 索引性能分析

Explain可以应用于SELECT、DELETE、INSERT、REPLACE、UPDATE语句

通过Explain关键字可以看到SELECT语句的执行计划,即可以查看到MySQL如何处理SELECT语句,通过Explain显示的结果来决定如何优化

具体的作用有

  1. 查看表的读取顺序
  2. 查看此语句可以使用哪些索引
  3. 此语句实际使用了哪些索引
  4. 查看此语句查询了多少行数据

explain语法

在任意的SELECT语句之前加上关键字 Explain或者Desc

EXPLAIN SELECT * FROM 表名;

使用Explain后返回的结果

查询结果的各个字段

Id ——select的查询序列号

表示查询中SQL执行的顺序;id相同时的执行顺序为从上到下;id不同时值越大越先执行

对于单表查询,查询一次一般会产生一个id的一行信息

explain select * from user;

对于多表查,查询一次一般会产生相同id的多行信息

 explain select * from career,user where career.id = user.career_id;

对于子查询,查询一次一般会产生不同id的多行信息

explain select * from user where user.career_id = (select id from career where id=1);

Select_type——select查询的类型

常见的取值有

SIMPLE:简单的select查询类型;查询语句中不包含子查询或UNION

PRIMARY:当查询中包含子查询或UNION时,即外层的查询为此查询类型

SUBQUERY:在SELECT或WHERE中包含了子查询时会被标记为此查询类型

DERIVED:在FROM列表中包含的子查询被标记为此查询类型(MySQL会将此子查询的查询结果作为临时表—派生表)

explain

select * from

 (select origo,count(*) as number from staff1 group by origo) as emp    

 where emp.number > 2; #根据居住地分组,并查询居住地人数大于2的(派生表的别名为emp)

UNION:在UNION中的第二个和随后的SELECT语句被标记为UNION(如果UNION被FROM子句中的子查询包含,则它的第一个SELECT会被标记为DERIVED)

explain

select origo,count(*) as number  from staff2 group by origo

union

select origo,count(*) as number  from staff1 group by origo;

explain

select origo,count(*) as number  from staff2 group by origo

union

select * from (select origo,count(*) as number from staff1 group by origo) as emp  where emp.number > 2;

 UNION RESULT:表示对应UNION的结果(UNION和UNRESULT一般会成对出现)

Table——表名称

显示这一行的数据是关于哪张表的,显示结果可能为表的名称、<derivedX>、<unionX1,X1>等

<derivedX>

当from子句中有子查询时,table列为<derivedX>的格式(x为id值),对应子查询返回的临时表(派生表)

<unionX1,X1>

当存在union时,union result的table列为<unionX1,X1>的格式;X1和X2表示参与union的表的id序号

Type——select的连接类型

select的连接类型是查看索引执行情况的一个重要指标,就是MySQL如何查找数据表中的记录

连接类型的性能由好到差为NULL、system、const、eq_ref、ref、fulltext、ref_or_null、index_merge、unique_subquery、index_subquery、range、index、all

重点关注的是:NULL、system、const、eq_ref、ref、range、index、all

在优化时尽量优化为性能好的(当查询时不查询任何表时才会出现NULL)

主键或唯一索引查询会出现const,使用非唯一性索引查询时会出现ref

一般我们最好保证查询时type达到range、ref级别

All和Index都是读全表,只是Index读的是索引树,All读的是数据表

不同连接类型代表的含义(通过Staff1表来模拟现象)

Fulltext:       当查询使用到全文索引时的连接类型

Ref_or_null: 类似于ref,也是非唯一性索引扫描;不过MySQL还会扫描哪些行包含了NULL

Index_merge: 表示使用了索引合并优化(即一个中使用到了多个索引)

Unique_subquery:类似于eq_ref,唯一性索引扫描;但是使用了IN查询,并且子查询查询字段为主键或唯一索引

Index_subquery:  类似于unique_subquery;不过子查询查询字段为非唯一索引

Null:   查询时不查询任何表(MySQL在优化阶段会分析查询语句,以此来判断是否需要访问表)或者在查询的值在此字段找不到,并且此字段建立了唯一索引

explain select min(id) from staff1; #查看主键的最小id

System:表只有一行记录;是Cost的特殊情况,平时不会出现可忽略

Const: 表示通过索引一次就找到了要查询的记录(一般存在于单表查询时,主键或唯一索引作为查询条件)

explain select * from staff1 where number=2021004;

 Eq_ref: 唯一性索引扫描;对于每个索引键,表中只有一条记录与之匹配;(一般存在于多表查询时,使用主键或唯一索引扫描作为查询条件)

explain select s1.*,a1.* from staff1 s1, account a1 where s1.id = a1.id;

 Ref:      非唯一性索引扫描;返回匹配某个单独值的所有行,可能会找到多个符合条件的行,属于查找和扫描的混合体(用于常规索引、联合索引情况)

explain select * from staff1 where origo='重庆';

Range:范围查询;当给一个字段添加索引之后,使用范围作为此字段的条件进行数据查询时的连接类型(般就是在where语句中出现了between、<、>、in等的查询)

explain select * from staff1 where number>2021001;

Index:index类型值遍历索引树(通过遍历索引树来查找数据,需要查找的字段都已经建立了索引-主键索引、唯一索引、常规索引等)

explain select id,number from staff1;

ALL:将遍历全表已找到匹配的行,没有使用索引

Possible_key ——显示可能应用在这张表的索引

该值为一个或多个

此字段显示的索引不一定会被查询使用到,可能会出现索引失效的问题

当Select语句发现可以使用多个索引的时候,可以通过SQL提示来建议Mysql语句使用指定的索引,可以避免SQL使用了性能比较低的索引(例如如果同时存在唯一索引和常规索引,可以建立SQL使用唯一索引)

Key——实际用到的索引

如果没有使用索引,则为NULL

哪些情况会导致有可用索引但是实际上没有使用到索引呢?

1、对于联合索引来说,没有遵守最左前缀法则

2、范围查询时使用到>或<,会导致范围查询右侧的列索引失效

3、在Where之后的索引列上进行运算操作(包含函数、比较运算符、谓词等)

4、字符串类型字段的值使用时,如果不加引号,存在隐式类型转换,索引将失效

5、当对头部进行模糊匹配时,索引会失效(即Like(%字符)或者Like(_字符))

6、用or分隔开的条件,如果or前条件中的列有索引,而后面的列中没有索引,那么or前面的索引不会被用到

7、数据分布影响;如果MySQL评估使用索引查询比全表查询更慢,则不使用索引,使用全表查询

如何规避索引失效呢?————具体在索引的优化介绍

1、联合索引遵守最左前缀法则,在创建联合索引时尽量将使用频率高的字段放在最左端

2、在范围查询时尽量使用过>=或者<=来规避范围查询

3、尽量不对索引列进行运算操作

4、在使用属于字符串类型的字段时,需要对其值加上引号

5、尽量使用尾部模糊匹配来代替头部模糊匹配;当对尾部进行模糊匹配时,则索引不会失效(即Like(字符%)或者Like(字符_))

6、只有当or前后都是用到索引时,索引才会失效

Key_len——实际索引使用到的字节数

表明了在索引中使用的字节数,通过此值可以大致估算出使用了索引中的哪些列

Key_len的计算规则

当字段允许为Null时,比不允许为Null大1个字节

不同的数据类型,占用的字节数时不同的,详情可以参考以下官方文档(介绍的是不允许为空的情况)

MySQL :: MySQL 8.0 Reference Manual :: 11.7 Data Type Storage Requirements

对于字符串数据类型来说,其占用的字节数还跟使用的字符编码有关

GBK               2字节

UTF8             3字节

ISO8859-1     1字节

GB2312         2字节

UTF-16          2字节

Ref    ——索引命中的列或常量

表进行数据查找时使用字段、常量、函数的结果等

常量:           const

空:              NULL

字段:           数据库名.表名.字段名

函数的结果:func(如果要想查看是哪个函数,可以在Explain语句后跟上SHOW WARNING语句)

explain select origo from staff1 where origo='重庆';

explain select * from staff1 where id in (select id from staff1 where id > 2);

Rows——预计select语句要检查的行数

mysql估计要读取并检查的行数;并不是结果的行数

在innoDB引擎中的表中,是一个估计值,不是很准确

Filtered——返回结果的行数占读取行数的百分比

该值越大越好

Extra——显示额外的信息

通过此字段显示的额外信息,也可以进行查询的优化(不同MySQL版本显示的内容可能会有些许差别)

Using Index:查找使用了索引,并且返回所需要的数据在该索引列中就可以找到(无需回表查询)

Using Where:先读取整行数据,再按照Where条件进行查询(符合就留下,不符合则丢弃)

Using Join Buffer:表示查询使用了连接缓冲(多用于多表连接查询)

Using Index Condition:查找使用了索引,但是需要回表查询数据—此种情况一般需要优化(可以使其满足覆盖索引条件来避免回表查询)

Using Temporary:查找使用了临时表(多见于group by语句)--此种情况一般需要优化(优先通过建立索引解决)

Using Filesort:通过表的索引或者全表扫描,读取满足条件的数据行,然后在排序缓冲区sort buffer中完成排序操作,所有不是通过索引直接返回排序结果的排序都叫FileSort排序(多见于order by语句)----此种情况一般需要优化(优先通过建立索引解决)

一般需要将Using Filesort、Using Temporary、Using Index Condition 等优化为Using Index


索引的使用规则

通过遵守索引的使用规则,避免索引失效;并且可以手动选择索引进行索引查询;使得索引的到最大利用

SQL提示

是优化数据库的一个重要手段,就是在SQL语句中加入一些人为的提示来达到优化操作的目的

SQL提示的字段

USE INDEX            建议数据库使用哪个索引(当一列属于多个索引类型式,建议此列使用哪种类型的索引,MySQL可能不会采用此建议)

IGNORE INEDX     告诉数据库不要用哪个索引      

FORCR INDEX       告诉数据库必须使用哪个索引

SQL提示的格式

SELECT 字段列表 FROM 表名 USE INDEX (索引名称) WHERE 判断条件; 

情景模拟

针对上述表,我们为origo、age创建了联合索引,现在我们再针对origo创建一个常规索引

 explain select origo from staff1 where origo='重庆' and age > 18; #此时我们查询此语句走的是联合索引  我们可以通过语句修改使其走单列索引

explain select origo from staff1 use index (as_origo) where origo='重庆' and age > 18;

最左前缀法则

主要针对联合索引,如果索引为联合索引,则要遵守最左前缀法则

最左前缀法则的要求

在使用联合索引进行查询时,查询从联合索引的最左列开始,并且不跳过索引中的列,可以跳过最右边的一列或多列;

在查询时,如果中途跳过了联合索引中的某一列,索引部分失效(此列之后的列索引失效),即无法进行索引查询,只可以进行全文查询

在查询时,如果最左边的列不存在,则不走索引,走全文扫描(即进行联合查询时必须包含最左列)  在查询时不用关心顺序,只要存在就可以了

create index as_origo on staff1(origo,age,name); #创建时由左到右创建,左边一般为查询频率高的

explain select * from staff1 where origo='重庆';

explain select * from staff1 where origo='重庆' and age = 22 ;    #查询时,也是从左到右查询;此时使用了索引

explain select * from staff1 where origo='重庆' and name='老六';   #此时origo使用了索引,name没有使用索引(通过key_len使用索引的字节数判断)

explain select * from staff1 where age = 22 ;   #此时没有使用索引(没有包含origo字段)

索引失效情况

范围查询

在联合索引中,出现范围查询(>或<)时,范围查询右侧的列索引失效

尽量使用过>=或者<=来规避范围查询

运算操作

在索引列上进行运算操作时,索引列将失效;运算包括使用函数、比较运算符、谓词等

字符串数据类型

字符串类型字段使用时,如果不对其值加引号,则存在隐式类型转换,索引将失效

Like字段模糊查询

当对尾部进行模糊匹配时,则索引不会失效(即Like(字符%)或者Like(字符_))

当对头部进行模糊匹配时,索引会失效(即Like(%字符)或者Like(_字符))

or连接条件

用or分隔开的条件,如果or前/后的条件中的列有索引,而后/前面的列中没有索引,那么or前/后面的索引不会被用到;

只有当or前后都是用到索引时,索引才会失效

数据分布影响

如果MySQL评估使用索引查询比全表查询更慢,则不使用索引,使用全表查询(一般用于大小判断的时候会出现)

由于B+树是顺序链表,当第一个叶子就符合或者前几个叶子就符合时,后面的叶子就必然也符合;此时MySql就判断使用全表查询更快,就会不适用索引,使用全表查询了

即:当要查询的结果占全表很大的比例时,可能就进行全表查询了

索引的设计原则

  1. 针对数据量较大(100多万数据及以上)、且查询比较频繁的表建立索引(很少查询,没有必要建立索引
  2. 针对于常作为查询条件(Where)、排序(Order By)、分组(Group By)操作的字段建立索引
  3. 尽量区分度高的列作为索引,尽量建立唯一索引;区分度越高,使用索引的效率越高
  4. 如果是字符串类型的字段,字段的长度较长,可以针对字段的特点,建立前缀索引
  5. 尽量使用联合索引,减少单列索引;查询时联合索引很多时候可以覆盖索引,节省存储空间,避免回表,提高查询效率
  6. 控制索引的数量,索引越多,维护索引结构的代价也就越大,会影响增删改的效率
  7. 如果索引列不能存储Null,就在创建表时使用NOT NULL约束此字段;当MySQL优化器知道每列是否包含NULL值时,它可以更好地确定哪个索引最有效地用于查询

总结

1、查询效率不高,首先使用explain分析:

如果发现没有索引,可以创建索引

如果发现是单列索引,要注意是否存在索引失效

如果发现是联合索引,要注意是否遵守最左匹配原则

2、尽可能地使得查询语句扫描更少地行数、表、列

3、如果对字符串创建了索引,尽可能减少字符串的长度(即为较短的字符串建立前缀索引)

4、尽量使得索引查询满足覆盖索引,避免回表查询

猜你喜欢

转载自blog.csdn.net/m0_49864110/article/details/132124128