索引失效的情况

111 阅读6分钟

索引失效的情况

数据准备

CREATE TABLE `tb_seller` (

`sellerid` VARCHAR (100),

`name` VARCHAR (100),

`nickname` VARCHAR (50),

`password` VARCHAR (60),

`status` VARCHAR (1),

`address` VARCHAR (100),

`createtime` DATETIME,

    PRIMARY KEY(`sellerid`)

)ENGINE=INNODB DEFAULT CHARSET=utf8mb4;

 

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('alibaba','阿里巴巴','阿里小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('baidu','百度科技有限公司','百度小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('huawei','华为科技有限公司','华为小店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('qf','微软中国','微软中国','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('qf1','千锋教育','千锋教育上海校区','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('luoji','罗技科技有限公司','罗技小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('oppo','OPPO科技有限公司','OPPO官方旗舰店','e10adc3949ba59abbe56e057f20f883e','0','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('ourpalm','掌趣科技股份有限公司','掌趣小店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('qiandu','千度科技','千度小店','e10adc3949ba59abbe56e057f20f883e','2','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('sina','新浪科技有限公司','新浪官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('xiaomi','小米科技','小米官方旗舰店','e10adc3949ba59abbe56e057f20f883e','1','西安市','2088-01-01 12:00:00');

INSERT INTO `tb_seller` (`sellerid`, `name`, `nickname`, `password`, `status`, `address`, `createtime`) VALUES('yijia','宜家家居','宜家家居旗舰店','e10adc3949ba59abbe56e057f20f883e','1','北京市','2088-01-01 12:00:00');

创建复合索引 CREATE INDEX index_name_status_address ON tb_seller(name,status,address)

前置知识

最左匹配原则

在 MySQL 中最左前缀法则的应用主要涉及到复合索引。当表定义了一个由多个列组成的复合索引时,最左前缀法则规定查询时必须使用索引的最左边的列开始,并且不能跳过索引中的列。如果查询中的条件不符合最左前缀法则,那么该索引将不会被使用。

后面的分析我们通过EXPLAIN执行计划来具体分析

索引失效的情况

不满足最左匹配原则

如果索引了多列(也就是复合索引),要遵守最左前缀法则。指的是查询从索引的最左前列开始,并且不跳过索引中的列。 匹配最左前缀法则,走索引;例如开始创建的复合索引的顺序是 从左到右 name、status、address

下面来测试:

  1. 满足最左匹配原则
  • 先单独根据name来查 EXPLAIN SELECT * FROM tb_seller WHERE name ='小米科技';

image.png 可以看到key确实是走了创建的复合索引,大小(key_len)是403

  • 根据 namestatus来查询EXPLAIN SELECT * FROM tb_seller WHERE name ='小米科技' AND status ='1'; image.png 此时也是满足的,此时key_len的大小是410
  • 根据 namestatusaddress来查询 EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status ='1' AND address ='北京市';

image.png 此时走了三个索引全部匹配 key_len 大小是813; 2. 不满最左匹配原则

  • 根据status来查 EXPLAIN SELECT * FROM tb_seller WHERE status ='1';

image.png

  • 根据address来查 EXPLAIN SELECT * FROM tb_seller WHERE address ='北京市'; image.png

通过statusaddress来查的时候由于不满足最左匹配原则,因此不走索引,此时索引失效

  • 根据 statusaddress来查询EXPLAIN SELECT * FROM tb_seller WHERE status ='1' AND address ='北京市';

image.png

范围查询右边的列,右边的列索引失效。

EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status >'1' AND address ='北京市';

先看结果

image.png 此时 根据namestatusaddress 来查询,key_len的大小为410,表示只有name和status是走了索引的而status右边的address没有走索引。

在索引列上进行运算操作, 索引失效

EXPLAIN SELECT * FROM tb_seller WHERE substring(name,3,2) ='科技' ;

image.png

字符串不加单引号,索引失效

EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status >'1'; image.png EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status >1;

image.png

通过这两句sql的分析第一句加了引号的情况,索引没有失效,而第二句没有加引号的情况只是name列走了索引,status列的索引失效了,这是因为,在查询时没有对字符串加单引号,MySQL的查询优化器,会自动的进行类型转换,造成索引失效。

尽量使用覆盖索引,避免select *

尽量使用覆盖索引(只访问索引的查询(索引列完全包含查询列)),减少select * 。 EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status ='1' AND address ='北京市';

image.png EXPLAIN SELECT NAME,STATUS,address FROM tb_seller WHERE NAME='小米科技' AND status ='1' AND address ='北京市';

image.png

以%开头的Like模糊查询,索引失效

如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。

image.png 解决方案 :

通过覆盖索引来解决:

image.png

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

explain select * from tb_seller where name='阿里巴巴' or createtime = '2088-01-01 12:00:00';

image.png

is NULL,is NOT NULL有时索引失效。

image.png 由于address这个字段都是非空,此时查询is not null的数据记录,mysql解析器认为根据address字段查询非空数据,走全表查询效率会更高,所以索引失效。

相反address这个字段没有空值数据,查询is null的空值数据,mysql解析器认为走索引更加高效,所以索引不会失效。