索引失效的几种情况

索引失效

本文参考尚硅谷视频

口诀:带头大哥不能死,中间兄都不能断,索引列上无操作,范围右边全失效,like百分加右边,字符串中有引号

准备:

建立员工记录表staffs(id,name,age,pos,add_time)

给表中name,age,pos字段添加索引(注意三个字段的顺序)

alter table staff
add index idx_staffs_nameAgePos(name,age,pos)

①最佳左前缀法则

指的是查询从索引的最左前列开始并且不跳过索引中的列

(带头大哥不能死,中间兄弟不能断)

(火车头带着车身跑,不能没有火车头,中间也不能缺少一段火车身)

例:

第一种情况,name字段索引一直被使用

mysql>EXPLAIN SELECT * FROM staffs WHERE NAME = ‘July’

在这里插入图片描述

上图可知,idx_staffs_nameAgePos索引有三个字段,name,age,pos

其中,只查找name可以使用索引

只查找name和age也可以使用索引

查找name,age,pos三个字段也可以使用索引

第二种情况,当不查找name而去查找后面age和pos字段时

在这里插入图片描述

索引失效了

第三种情况,查找了name字段和pos字段,而缺少了中间的age字段

在这里插入图片描述

上图对比了分别查找name字段和name,pos字段的两种情况,发现虽然都用到了idx_staffs_nameAgePos索引,但是key_len长度没变,ref索引用到的字段也没变,说明针对字段pos的索引失效了,原因是缺少了中间的age索引字段

②不要再索引列上做任何操作

不要在索引列上左任何操作(计算,函数,自动or手动类型转换),会导致索引失效转换为全表扫描

例:

在这里插入图片描述

上图可以看到,没用left函数之前,是一个正常的索引引用,使用函数之后,索引失效了

③存储引擎不能使用索引中范围条件右边的列

与上面单表索引优化分析中的例子一样

在这里插入图片描述

第一个查询全等值匹配没问题

第二个查询中间使用了范围条件,结果导致manger的索引失效

按照BTree索引的工作原理,先排序name,如果遇到相同的name则再排序age,如果遇到相同的age则再排序pos,当age字段在复合索引中间时,因age>25条件是一个范围值(所谓range),MySQL无法利用索引再对后面的pos部分进行建索,即range类型查询字段后面的索引无效

④尽量使用覆盖索引

只访问索引列的查询,索引列和查询列一致,避免select *

在这里插入图片描述

当只访问了索引列后,Extra中出现了Using index,表明是覆盖查询,这是好的

⑤Mysql在使用(<>或!=或is null或is not null)时无法使用索引会导致全表扫描(现在改了)

注:新版本的MySQL中这些已经可以使用索引了

在这里插入图片描述

⑥like以通配符(%)开头的索引会失效变成全表扫描

以%开头会失效,变成全表扫描

通配符出现最前面说明所有都适配,所有都要扫描

在这里插入图片描述

不以%开头则不失效

在这里插入图片描述

如何解决?

可以使用覆盖索引

tb1_user表中有id,name,age,email四个字段,对name,age字段设置索引,id自动为主键索引

那么查询id,name,age字段都是覆盖索引,都可以使索引生效

在这里插入图片描述

但是如果使用explain select * from tb1_user where name like '%aa%'就会索引失效,因为此时不是覆盖索引,不是覆盖索引使用%会失效

⑦字符串不加单引号会导致索引失效

name字段为varchar类型

假设有一行name=‘2000’,这时如果用name=2000条件查找也会有结果

在这里插入图片描述

用EXPLAIN分析

在这里插入图片描述

可以看出,用name=2000作为判断条件,不加单引号导致索引失效了,为什么?

因为name=2000也可以查出来是因为MySQL在底层做了一次类型转换,把整形2000转换成了字符串’2000’

违背了上面第②条的不要在索引列上左任何操作的原则,所以索引失效了

练习

在这里插入图片描述

2.条件顺序改变

假设表student中id,name,age三个字段建立了一个复合索引

那么条件列的顺序换换索引是否还能有效?

explain select * from student where id = 1 and name = "张三" and age = 22explain select * from student where name = "张三"  and id = 1 and age = 22

答案:yes,M有SQL的优化器会自动给列排序为创建索引时的列,但是还是建议顺序与索引列顺序一致,提高效率

3.order by 只排序,不查询

建表test03(c1,c2,c3,c4)

对c1,c2,c3,c4加复合索引

在这里插入图片描述

先是MySQL优化器对where后面的条件进行排序,c1,c2,c3,c4

由于索引功能有两个,分别是排序与查询,对c3进行排序并没有查询,但也是用到了索引,所以实际上c1,c2,c3都用到了索引,而c4由于c3没有等值判断所以用不到索引,索引对c4失效了。

4.order by 中的字段顺序不能调换

在这里插入图片描述

由order by 的特性可知,order先对c3排序后再对c2排序,所以优化器不会把c3,c2顺序互换,这就导致了最终的是顺序是c1,c3,c2,c5

只有c1用到了索引,c3与c1中间缺少了c2,所以c3用不到索引,c5本身就没有索引

所以对c3和c2的排序没有用到内部索引,使用了外部索引Using filesort

在这里插入图片描述

这里虽然order by 中是先c3再c2,但是前面条件查询时已经用到了c2,所以实际用到索引的顺序是c1,c2,c3

所以c1,c2都使用到了索引,ref为两个const,Extra也没有出现Using filesort

5.group by先排序后分组,用法与order by一样
6.like的%位置关系

在这里插入图片描述

第一条sql语句与第四条效果一样,range范围类型,用到了三个索引列,ref为null

为什么这里ref为null?明明三个索引都被使用了,前文提到过,range范围后的索引都会失效,但是like是特例,由于%不在第一个位置上,第一个位置上的是k,k是一个确定的值,所以可以算作查询被使用索引,所以能连通c3,c3也能使用索引

第二条sql语句与第三条效果一样,ref一对多结果,用到了一个索引列,ref为const

为什么这里ref为const

因为like后面%在第一个位置上,导致like后面的索引全失效,所以只有c1的索引有效

小表驱动大表

5 X 1000 == 1000 X 5 ,但是在数据库中第一种比第二种好,连接5次数据库,每次查1000条,连接1000次数据库,每次查5条,肯定第一种好

举例:查找华为公司各部门的员工信息(员工表A,部门表B)

第一种:用in函数(A表数量大于B表,用in,先查in括号里面的少的表)

select * from A where A.id in (select B.id from B)

第一步:select B.id from B

第二步:select * from A where A.id = B.id

部门表的数量肯定比员工表少,所以用部门表驱动员工表,小表驱动大表

如果先查员工表,那么需要对员工表进行全表搜索,相比较部门表,肯定效率低

第二种:用exist函数(B表数量小于A表,先查B表,再判断B表中是否有满足条件的数据)

select * from B where exists(select 1 from A where A.id = B.id)

第一步:select * from B

第二步:select * from B where A.id = B.id

exist()子查询只返回true或false,因此select 1也可以是任意select * ,官方说法是忽略select清单

select ... from table where exist(subquery)

该语法可以理解为:将主查询的数据放到子查询中做条件验证,根据验证结果(true或false)来决定主查询的数据结果是否得以保留

排序优化

1.order by后面的顺序必须和索引顺序一致,否则会造成使用order by的内部排序(file sort)

#建表
create table tblA(
	#id int primary key not null auto_increment,
	age int,
	birth TIMESTAMP not null
)
#创建age,birth的索引
create index idx_tblA_age_birth on tblA(age,birth)

#1.只对age排序,没问题,用上索引
explain select * from tblA where age > 20 order by age
#2.先对age排序,再对birth排序,没问题
explain select * from tblA where age > 20 order by age,birth
#3.顺序一变,出现了filesort,危险的文件排序,索引失效
explain select * from tblA where age > 20 order by birth,age

#4.带头大哥age不在了,所以索引失效,用到了索引,是索引失效
explain select * from tblA where birth > '2021-10-10 00:00:00' order by birth
#5.带头大哥存在,索引生效
explain select * from tblA where birth > '2021-10-10 00:00:00' order by age
#6.age升序,birth降序,打乱了索引的结构,索引失效
explain select * from tblA order by age asc , birth desc

order by支持两种方式的文件排序,FileSort和index,index效率高,filesort效率低

能成功使用index的情况:1.满足最左前缀法则2.排序统一

2.filesort有两种排序算法,可能是单路排序的缓存空间太小导致效率降低

双路排序(mysql4.1之前):两次扫描磁盘,读取待排序的那一列,I/O非常耗时,所以效率低

单路排序:从磁盘中读取查询需要的所有列,按照order by在buffer对它们进行排序,然后扫描排序后的列表进行输出。它的效率更快,因为第一次读取数据后放到了缓冲中,把随机I/O变成了顺序I/O,但是会占用更多的内存空间

单路排序可能出现的问题:

单路算法总体好于双路算法,但是如果一次读取不完所有数据,会造成多次读取,反而效率还低于多路算法,如果sort_buffer_size比较小,单路排序每次只能取出sort_buffer_size大小的数据,取不完就要多次取。

多路算法也可能超出缓冲区,但是单路算法的风险更大

3.order by 排序不要用select *

因为select * 查询所有的,占用的排序缓冲区太大,在单路算法下,很可能超出缓冲区大小,就会多次I/O读取,降低效率

show profiles诊断sql

show profiles:查看之前执行的所有sql及运行时间

1.先执行一条sql

select * from emp where id = 4

2.进行show profiles,可以看到刚才执行的那条sql的id为52

show profiles

请添加图片描述

3.查看刚才那条语句在内存中详细的运行状态

show profile cpu,block io for query 52;

请添加图片描述

常用type

all: 显示所有的开销信息

block io: 显示块IO相关开销

context switches: 上下文切换相关开销

cpu: cpu相关开销

ipc: 显示发送和接收相关开销

memory: 显示内存相关开销信息

page faults: 显示错误页面相关开销信息

source: 显示和source_function,source_file,source_line相关的开销信息

swaps: 显示交换次数相关开销的信息

如果查询详细的运行状态中有一下四个中的任何一个,必须优化

1.converting HEAP to MyISAM :查询结果太大,内存不够用了,往磁盘上搬

2.Create tmp table :创建临时表。(数据拷贝到临时表,用完再删除)

3.Copying to tmp table on disk:把内存临时表复制到磁盘,危险

4.locked:锁住了

  • 16
    点赞
  • 89
    收藏
    觉得还不错? 一键收藏
  • 3
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值