目录

explain

索引使用

验证索引效率

索引建立的准则

最左前缀法则

 范围查询

 索引失效情況

数据分布影响


我们之前介绍过查询sql的时间的查询。MySQL进阶-sql执行时间的查询_兜兜转转m的博客-CSDN博客

其实开发人员主要还是使用explain。

explain

EXPLAIN 或者 DESC命令获取 MySQL 如何执行 SELECT 语句的信息,包括在 SELECT 语句执行 过程中表如何连接和连接的顺序。

-- 直接在select语句之前加上关键字 explain / desc
EXPLAIN SELECT 字段列表 FROM 表名 WHERE 条件 ;

日期做索引不生效 索引设置失效时间_字段

 Explain 执行计划中各个字段的含义:

日期做索引不生效 索引设置失效时间_java_02

日期做索引不生效 索引设置失效时间_字段_03

其中重要的是,type,key,和key_len。

索引使用

验证索引效率

在讲解索引的使用原则之前,先通过一个简单的案例,来验证一下索引,看看是否能够通过索引来提升 数据查询性能。在演示的时候,我们还是使用之前准备的一张表 tb_sku , 在这张表中准备了1000w 的记录。 

日期做索引不生效 索引设置失效时间_日期做索引不生效_04

这张表中id为主键,有主键索引,而其他字段是没有建立索引的。 我们先来查询其中的一条记录,看 看里面的字段情况,执行如下SQL:

select * from tb_sku where id = 1\G

日期做索引不生效 索引设置失效时间_数据库_05

可以看到即使有1000w的数据,根据id进行数据查询,性能依然很快,因为主键id是有索引的。 那么接 下来,我们再来根据 sn 字段进行查询,执行如下SQL:

SELECT * FROM tb_sku WHERE sn = '100000003145001';

那么我们可以针对于sn字段,建立一个索引,建立了索引之后,我们再次根据sn进行查询,再来看一 下查询耗时情况。

建立索引

create index idx_sku_sn on tb_sku(sn) ;
# create index 索引名 on 表名(索引字段);

然后再次执行相同的SQL语句,再次查看SQL的耗时。

SELECT * FROM tb_sku WHERE sn = '100000003145001';

日期做索引不生效 索引设置失效时间_mysql_06

 我们明显会看到,sn字段建立了索引之后,查询性能大大提升。建立索引前后,查询耗时都不是一个数 量级的。这就是建立索引的魅力。

索引建立的准则

最左前缀法则

如果索引了多列(联合索引),要遵守最左前缀法则。

  • 最左前缀法则指的是查询从索引的最左列开始并且不跳过索引中的列
  • 如果跳跃某一列,索引将会部分失效(后面的字段索引失效)。

具体是什么意思呢?

首先我们使用上面的建立索引语句,建立如下索引。

日期做索引不生效 索引设置失效时间_mysql_07

我们现在主要分析这个联合索引来具体理解一下什么是最左前缀法则。

在 tb_user 表中,有一个联合索引,这个联合索引涉及到三个字段,顺序分别为:profession, age,status。

create index idx_user_pro_age_sta on tb_user(profession,age,status);

对于最左前缀法则指的是,查询时,最左边的列,也就是profession必须存在否则索引全部失效而且中间不能跳过某一列,否则该列后面的字段索引将失效。 接下来,我们来演示几组案例,看一下 具体的执行计划:

explain select * from tb_user where profession = '软件工程' and age = 31 and status
= '0';

日期做索引不生效 索引设置失效时间_字段_08

我们可以看出索引类型为ref,索引名为:idex_user_pro_age_sta,长度为42。

我们去掉status = ‘0’;

explain select * from tb_user where profession = '软件工程' and age = 31

 

日期做索引不生效 索引设置失效时间_日期做索引不生效_09

我们发现其他没变,而是长度减少了4,这个4就是status的长度。

explain select * from tb_user where profession = '软件工程';

日期做索引不生效 索引设置失效时间_java_10

 长度变为了36.

以上的这三组测试中,我们发现只要联合索引最左边的字段 profession存在,索引就会生效,只不 过索引的长度不同。 而且由以上三组测试,我们也可以推测出profession字段索引长度为36、age 字段索引长度为2、status字段索引长度为4

接下来我们去掉profession = '软件工程' 

explain select * from tb_user where age = 31 and status = '0';

日期做索引不生效 索引设置失效时间_mysql_11

我们可以发现type变为all,表示全表扫描没有使用索引,这是因为不符合最左前缀法则,因此索引没有生效,这是非常耗时的。

explain select * from tb_user where profession = '软件工程' and status = '0';

日期做索引不生效 索引设置失效时间_mysql_12

可以看出,走了索引,但是长度是36,因此只有profession走了索引,后面的status失效了。 

上述的SQL查询时,存在profession字段,最左边的列是存在的,索引满足最左前缀法则的基本条 件。但是查询时,跳过了age这个列,所以后面的列索引是不会使用的,也就是索引部分生效,所以索 引的长度就是36.

 范围查询

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

explain select * from tb_user where profession = '软件工程' and age > 30 and status
= '0';

日期做索引不生效 索引设置失效时间_日期做索引不生效_13

当范围查询使用> 或 < 时,走联合索引了,但是索引的长度为38,就说明范围查询右边的status字 段是没有走索引的。

但是改为age>= 时就走了索引。

explain select * from tb_user where profession = '软件工程' and age >= 30 and
status = '0'

日期做索引不生效 索引设置失效时间_字段_14

 索引失效情況

  • 准则1:不要在所有列上进行函数操作。
  • 准则2:字符串列不加引号
  • 准则3:模糊查询,而且是头模糊,尾部模糊不会失效。
  • 准则4:or连接条件:用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会 被用到。
  • 准则5:数据分布影响

准则1:不要在所有列上进行函数操作。

A. 当根据phone字段进行等值匹配查询时, 索引生效。

explain select * from tb_user where phone = '17799990015';

B. 当根据phone字段进行函数运算操作之后,索引失效。

explain select * from tb_user where substring(phone,10,2) = '15';

日期做索引不生效 索引设置失效时间_字段_15

 准则2:字符串列不加引号

explain select * from tb_user where phone =17799990015;

日期做索引不生效 索引设置失效时间_字段_16

 准则3:模糊查询,而且是头模糊,尾部模糊不会失效。

尾部

explain select id from tb_user where profession like "软件%";

日期做索引不生效 索引设置失效时间_日期做索引不生效_17

头部:

日期做索引不生效 索引设置失效时间_mysql_18

 准则4:or连接条件:用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会 被用到

explain select * from tb_user where id = 10 or age = 23;
explain select * from tb_user where phone = '17799990017' or age = 23;

日期做索引不生效 索引设置失效时间_日期做索引不生效_19

日期做索引不生效 索引设置失效时间_java_20

由于age没有索引,所以即使id、phone有索引,索引也会失效。所以需要针对于age也要建立索引。

数据分布影响

如果MySQL评估使用索引比全表更慢,则不使用索引。

一种是全表,第二种是走的索引

就是因为MySQL在查询时,会评估使用索引的效率与走全表扫描的效率,如果走全表扫描更快,则放弃 索引,走全表扫描。 因为索引是用来索引少量数据的,如果通过索引查询返回大批量的数据,则还不 如走全表扫描来的快,此时索引就会失效。

select * from tb_user where phone >= '17799990005';
select * from tb_user where phone >= '17799990015';