几个必须掌握的SQL优化技巧(七):索引的最佳使用法则

小知识,大挑战!本文正在参与“   程序员必备小知识   ”创作活动

作者的其他平台:

| CSDN:blog.csdn.net/qq_4115394…

| 掘金:juejin.cn/user/651387…

| 知乎:www.zhihu.com/people/1024…

| GitHub:github.com/JiangXia-10…

| 公众号:1024笔记

本文大概5073字,读完共需12分钟

1 前言

在应用的开发过程中,由于开发初期的数据量一般都比较小,所以开发过程中一般都比较注重功能上的实现,但是当完成了一个应用或者系统之后,随着生产数据量的急剧增长,那么之前的很多sql语句的写法就会显现出一定的性能问题,对生产的影响也会越来越大,这些不恰当的sql语句就会成为整个系统性能的瓶颈,为了追求系统的极致性能,必须要对它们进行优化。

2 正文

前面一篇文章几个必须掌握的SQL优化技巧(六):针对SQL语句的优化介绍了数据库优化中关于sql语句的几种优化方式,索引也是数据库优化中最重要也是最高效的手段之一,通过索引通常可以帮助用户解决大多数mysql的性能优化问题。这篇文章就来介绍如何对索引进行优化。

首先我们来看看索引对查询效率是否有提升以及提升效果如何。

在tb_item表中有300万条数据:

图片

在tb_item表中id是主键索引,通过id作为查询条件可以看到查询速度很快,基本没有查询时间:

图片

而如果将title非索引字段作为查询条件可以看到,查询时间是比较慢的:

图片

在对title字段也创建索引之后,查询效率明显提升了,所以索引的使用对于数据的查询性能具有很明显的提升。

图片

但是并非只要创建了索引就能够提升查询效率,只有创建了索引并且合理利用了索引对查询效率才能有明显的提升。

在索引的使用中需要注意以下几点:

1、全值匹配,对索引中所有列都指定具体的值

比如 user表中name、code和email都是索引,那么在查询中如果给这三个索引都指定查询的值,那么执行效率比较高:

select * from user where name='张三' and code='1001' and email='[email protected]'
复制代码

2、最左前缀法则

如果索引了多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始,并且不跳过索引中的列,比如user表中创建了复合索引:

create index idx_user_name_code_email on user(name,code,email)
复制代码

那么最左前缀法则值得就是索引从最左边的name开始并且不能跳过code直接到email。

比如,下面这三句sql就是符合最左前缀法则的

select * from user where name='张三' 
select * from user where name='张三' and code='1001' 
select * from user where name='张三' and code='1001' and email='[email protected]'
复制代码

但是下面的就不符合最左前缀法则了:

select * from user where code='1001' and email='[email protected]'
select * from user where email='[email protected]'
select * from user where name='张三' and email='[email protected]'
复制代码

不符合最左前缀法则的sql语句就会导致索引失效。

但是下面这句是复合最左前缀法则:

select * from user where code='1001' and email='[email protected]' and name='张三'
复制代码

另外对于:

select * from user where name='张三' and email='[email protected]
复制代码

上面这条sql其实也会走索引,但是只会走name索引,而email索引会失效。

所以最左前缀法则值得是后面的where条件包不包含最左边的索引字段,而与where条件后面索引的顺序无关的,如果where条件后面包含最左列索引,并且没有跳过其他索引,那么会走索引,否则会使索引失效。这就好比闯关游戏,闯关需要从第一关到最后一关按照顺序来闯关,不能跳过第一关直接到第二关,也不能第一关闯完,直接跳过第二关来到第三关。

3、范围查询右边的列,索引会失效

指的是在复合索引中,如果某个索引使用了范围查询,那么这个索引后面的索引会失效,比如:

select * from user where name='张三' and code>'1001' and email='[email protected]'
复制代码

上面这条语句只有name和code索引生效,但是email索引会失效了。

4、在索引列上进行函数运算操作,索引会失效

比如:

select * from user where substring(name,0,2) = '张三' 
复制代码

上面这样的sql语句,会使得name索引失效。

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

比如:

select * from user where name='张三' and code=1001
复制代码

上面的语句虽然能查询数据,但是其实后面的code并没有走到索引,索引只有name,因为表中code是varchar类型的,但是这里是int类型,所以在查询的时候,sql在执行的时候对code进行了隐式转换转换为varchar,其实就等同于第四点在索引列上进行函数运算操作,导致了索引会失效。

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

在进行查询的时候尽量使用覆盖索引即只访问索引的查询(索引列完全包含查询列),而尽量不要使用select * 进行查询,如果查询列的字段超出了索引列,也会降低性能。比如name,code,email三个字段是索引字段,那么

select * from user where name='张三'
select name,code,email,password from user where name='张三'
复制代码

select name,code,email from user where name='张三'
复制代码

的查询效率都低。因为select *和查询字段超过了索引字段的查询中,虽然用到了索引,但是需要进行回表查询数据。即先查询索引的字段信息,但是并不是包含所有的数据信息,而是根据查询索引字段的信息从而获取该一行所有的数据。

7、关于like模糊查询

在使用like进行模糊查询的时候,如果以%开头的like进行模糊查询,那么会导致索引失效,而如果仅仅是%在尾部进行模糊匹配,索引不会失效。比如

select * from user where name='%张三'
select * from user where name='%张三%'
复制代码

会导致索引失效,而

select * from user where name='张三%'
复制代码

的索引还是会有用。所以进行模糊匹配尽量匹配的值头部不要加%。但是可以通过覆盖索引来使得这种情况的索引生效。比如

select id,name,code,email from user where name='%张三'
复制代码

这种情况下,索引还是会生效的。

8、关于or的使用

在用or分割开的条件,如果or前的条件中的列有索引,而后面的列中没有索引,那么涉及的索引都不会被用到,比如如果name是索引列,而password字段不是索引列,那么中间是用or进行条件连接是不走索引的,索引失效

select * from user where name='张三' or password='123456'
复制代码

如果中间使用and,那么还是会用到索引:

select * from user where name='张三' and password='123456'
复制代码

9、关于is null 和 is not null的使用

在对于索引字段使用is null 或者is not null 作为条件查询的时候,有时候会走索引,有时候会使索引失效,比如user表中的name字段没有空值,那么如果

select * from user where name is null 
复制代码

这时候name字段的值是没有null或者null值是比较少的,底层在进行评估的的时候会走索引;而如果条件是 is not null,因为该字段的值都是is not null或者绝大部分值是is not null,那么索引就会失效,这时候会走全表扫描的查询速度会更快。is null 和is not null,反过来也是同样的。

10、关于in和 not in的使用

使用in会走索引,而使用not in索引会失效。

走索引:

select * from user where name in ('张三','张四','张五')
复制代码

索引失效:

select * from user where name not in ('张三','张四','张五')
复制代码

11、关于单列索引和复合索引的选择

在创建sql语句的时候,尽量使用复合索引,而少使用单列索引。

因为,比如在创建一个复合索引的时候:

create index idx_user_name_code_email on user(name,code,email)
复制代码

其实相当于创建了三个索引:name、name+code、name+code+email;

而创建三个单列索引:

create index idx_user_name on user(name)
create index idx_user_code on user(code)
create index idx_user_email on user(email)
复制代码

这时候如果在对这三个索引字段进行查询的时候,可能使用到的索引是上面三个单列索引,但是实际上数据库底层评估会根据字段的数据的辨识度选择一个最优的索引来使用,而不会使用全部的索引。

对于复合索引的话,里面的字段也不是越多就越好,索引包含过多的字段,会导致索引臃肿庞大,维护的开销也会因此增大,所以如果用于筛选的字段有很多,那么我们就要选出最大价值的筛选字段来作为复合索引的一份子。比如A字段的数据重复率低,B字段的数据重复率高,那么A就比B更有资格参与复合索引的建设,同时为了避免索引维护开销过大,建议非唯一索引中包含的字段不要超过5个,那么正常情况下,需要优先选择辨识度高的那5个字段,从高到低排列。

最后可以通过以下的命令来查看索引的使用情况:

-- 查看当前会话的索引使用情况
show status like 'Handler_read%'

-- 查看全局的索引使用情况
show global status like 'Handler_read%'
复制代码

图片

在这里会涉及到几个参数指标:

Handler_read_fist:索引中第一条被读的次数,如果较高,表示服务器正执行大量索引扫描,这个值越低越好;

Handler_read_key:如果索引正在工作,这个值代表一个行被索引值读的次数,如果这个值越低,表示索引得到的性能改善不高,因为索引不经常被使用,所以这个值越高越好;

Handler_read_next:按照键顺序读下一行的请求数,如果你用范围约束或如果执行索引扫描来查询索引列会导致该值增加;

Handler_read_prev:按照键顺序读前一行的请求数。该读方法主要用于优化 order by desc;

Handler_read_rnd:根据固定位置读一行的请求数,如果你正执行大量查询并需要对结果进行排序该值较高,则可能使用了大量需要mysql扫描整个表的查询或连接没有正确使用键,这个值较高,意味着运行效率较低,应该建立索引来优化;

Handler_read_rnd_next:在数据文件中读下一行的请求数,如果你正进行大量的表扫描,该值较高。通常说明你的表索引不正确或者写入的查询并没有利用索引。

该方法对于索引优化可以起到一个参考的作用。

3 总结

上面就是在数据库优化中关于索引的使用和优化的全部内容,这里只是总结了工作中了解到的一些关于索引优化的原则,还有一些其他的优化办法需要我们在日常的工作和学习中,不断的总结和积累。

至此,关于SQL优化技巧系列的文章已经全部写完了,后面也还会写一些其他关于数据库相关的文章。

最后如果觉得本文写得不错,就推荐给更多的人呗。

今日推荐

几个必须掌握的SQL优化技巧(一):查看SQL语句的执行频率

几个必须掌握的SQL优化技巧(二):如何定位低效率执行SQL

几个必须掌握的SQL优化技巧(三):Explain分析执行计划

几个必须掌握的SQL优化技巧(四):使用Trace工具分析优化器执行计划

几个必须掌握的SQL优化技巧(五):Show Profile分析SQL性能

几个必须掌握的SQL优化技巧(六):针对SQL语句的优化

                 我就知道你在看!

本文使用 文章同步助手 同步

猜你喜欢

转载自juejin.im/post/7018161891881156621
今日推荐