索引失效的情况
数据准备
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
下面来测试:
- 满足最左匹配原则
- 先单独根据
name来查EXPLAIN SELECT * FROM tb_seller WHERE name ='小米科技';
可以看到key确实是走了创建的复合索引,大小(key_len)是403
- 根据
name和status来查询EXPLAIN SELECT * FROM tb_seller WHERE name ='小米科技' AND status ='1';此时也是满足的,此时key_len的大小是410
- 根据
name和status和address来查询EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status ='1' AND address ='北京市';
此时走了三个索引全部匹配 key_len 大小是813;
2. 不满最左匹配原则
- 根据
status来查EXPLAIN SELECT * FROM tb_seller WHERE status ='1';
- 根据
address来查EXPLAIN SELECT * FROM tb_seller WHERE address ='北京市';
通过status和address来查的时候由于不满足最左匹配原则,因此不走索引,此时索引失效
- 根据
status和address来查询EXPLAIN SELECT * FROM tb_seller WHERE status ='1' AND address ='北京市';
范围查询右边的列,右边的列索引失效。
EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status >'1' AND address ='北京市';
先看结果
此时 根据
name和status和address 来查询,key_len的大小为410,表示只有name和status是走了索引的而status右边的address没有走索引。
在索引列上进行运算操作, 索引失效
EXPLAIN SELECT * FROM tb_seller WHERE substring(name,3,2) ='科技' ;
字符串不加单引号,索引失效
EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status >'1';
EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status >1;
通过这两句sql的分析第一句加了引号的情况,索引没有失效,而第二句没有加引号的情况只是name列走了索引,status列的索引失效了,这是因为,在查询时没有对字符串加单引号,MySQL的查询优化器,会自动的进行类型转换,造成索引失效。
尽量使用覆盖索引,避免select *
尽量使用覆盖索引(只访问索引的查询(索引列完全包含查询列)),减少select * 。
EXPLAIN SELECT * FROM tb_seller WHERE NAME='小米科技' AND status ='1' AND address ='北京市';
EXPLAIN SELECT NAME,STATUS,address FROM tb_seller WHERE NAME='小米科技' AND status ='1' AND address ='北京市';
以%开头的Like模糊查询,索引失效
如果仅仅是尾部模糊匹配,索引不会失效。如果是头部模糊匹配,索引失效。
解决方案 :
通过覆盖索引来解决:
用or分割开的条件, 如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到。
explain select * from tb_seller where name='阿里巴巴' or createtime = '2088-01-01 12:00:00';
is NULL,is NOT NULL有时索引失效。
由于address这个字段都是非空,此时查询is not null的数据记录,mysql解析器认为根据address字段查询非空数据,走全表查询效率会更高,所以索引失效。
相反address这个字段没有空值数据,查询is null的空值数据,mysql解析器认为走索引更加高效,所以索引不会失效。