:Sql实战调优场景剖析(下)

Posted 风清扬逍遥子

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了:Sql实战调优场景剖析(下)相关的知识,希望对你有一定的参考价值。

        上一章我们讲述了索引调优实战在Join的过程,那么本章重点阐述索引失效的场景及原因剖析!

1、索引失效场景

        老规矩先导入一些表作为数据使用,表的所有定义在这个链接中:

mysql高级调优篇表补充——建表SQL_风清扬逍遥子的博客-CSDN博客⭐️tbl_emp⭐️CREATE TABLE `tbl_emp` (`id` int(11) NOT NULL AUTO_INCREMENT,`name` varchar(20) DEFAULT NULL,`deptId` int(11) DEFAULT NULL,PRIMARY KEY (`id`) ,KEY `fk_dept_id`(`deptId`))ENGINE = InnoDB AUTO_INCREMENT = 1 CHARACTER SET = utf8;⭐️tbl_dhttps://blog.csdn.net/qq_31821733/article/details/120886389?spm=1001.2014.3001.5501        导入staffs员工表:

1.1、全值匹配我最爱

        我们添加了索引:

        看第一个Sql:select * from staffs where name='July'; 

        我建立了复合索引分别是name,age,pos,我只查一个,索引会不会失效?

        看下执行计划可以知道走索引上,你建立三个,我走了1个,没问题

        再来一个Sql:select * from staffs where name='July' and age = 25; 

        执行计划知道,走部分索引一样没问题。 

        但是不知道有没有发现一个问题!!Extra为Null??这个是第一次出现,那么这里我要重点强调下,Extra为Null的时候,如果走了索引,说明这个查询,进行了回表!!

        那么什么是回表呢?简单来说,如果你查询的字段,存在非索引字段,那么查询的时候,Mysql虽然根据了你的条件得到了这个记录,但是不在索引的字段无法通过索引的方式直接得到,只能通过拿到该条记录的主键索引,再从数据行里读,我们知道Mysql索引文件和数据文件是在两个不同的文件里的,要去读磁盘;所以索引文件建立的效果,就是帮助我们对数据进行排序和查找效率的优化,不至于去读数据行进行额外的IO开销;

        所以这里字段我用select *,因为复合索引里没有add_time这个字段,所以无法直接查出来add_time这个列的记录,要通过定位到主键,然后再读一次数据行才可以得到这个记录,称为回表。

        如果我这么写,就不会出现回表,因为pos在索引列中!

         而查add_time,就回表了:

         那我再加一个字段pos,很明显精度越高,key_len的长度就越长。总结是【全值匹配我最爱】

        现在三种2情况都正常,我们来看一些特殊场景!

        explain select * from staffs where age = 23 and pos = 'dev'; 执行计划走一下发现

        索引怎么不走了?再看看这个sql:select * from staffs where pos = 'dev';

        索引也不走!

         再来一个sql:select * from staffs where name = 'zhangsan';走索引了

         总结下来:如果查询中没有开头的索引,也就是不告诉我1楼在哪里,让我从2楼或者3口开始找索引,不好意思,我找不到,只能全表扫!违背了【最佳左前缀法则】

        回顾下我们建立的索引,(name, age, pos)顺序是1楼,2楼,3楼;如果索引了多列,要遵守最左前缀法则,指的是查询从索引的最左前列开始并且不跳过索引中的列。即【带头大哥不能死】

        再看下这个sql:select * from staffs where name = 'zhangsan' and pos = 'dev'; 这个sql等于中间2楼断开了,执行计划显示这个key_len和只有name的时候一样,说明只走了name索引,Extra中出现Using index condition,这个是5.6后新加的特性,会先条件过滤索引,过滤完索引后找到所有符合索引条件的数据行,随后用 WHERE 子句中的其他条件去过滤这些数据行;其实没有什么用,就是走到了索引上的意思。【中间兄弟不能断】

         总结:全值匹配我最爱,最左前缀要遵守;带头大哥不能死,中间兄弟不能断;

1.2、勿在索引列做任何操作

        不要在索引列上做任何操作,包括计算,函数,自动或者手动类型转换,会导致索引失效而转向全表扫描。

        举例子:explain select * from staffs where left(name, 4) = 'July'; 查找name左往右4个字符为July的行。索引失效了!

        总结:【索引列上少计算】

 1.3、范围之后全失效

        这个上一节提过,like后面的索引会失效,即Mysql存储引擎不能使用索引中,范围条件右边的列!

        举例子,explain select * from staffs where name = 'July' and age > 14 and pos = 'manager'; 因为2楼已经是范围查找了,age用到了索引,进行范围查找,但是后面的索引pos就失效了,因为你告诉我2楼,没告诉我去3楼的路,所以我找不到3楼的目录,定位不到3楼,只能挨个去找入口!这里要注意,5.7以前的优化,是如果出现了范围查找,则当前范围的索引也不走,而5.7后,范围索引之后的才失效,所以这里的key_len=78,单个name话是74,三个都走是140。

 1.4、尽量使用覆盖索引

        查询语句中只访问索引的查询,索引列和查询列尽量保持一致,或者查询列<=索引列:

        看这个sql:explain select * from staffs where name = 'July' and age = 15 and pos = 'manager'; 如果用了* ,我说过会回表!但是指定了索引的列,那么就走了覆盖索引,Using index是很好的效果! 

        包括以下的场景也是一样!

1.5、不等于场景下索引失效

        Mysql在使用不等于的场景下,无法使用索引导致全表扫描

        explain select * from staffs where name != 'July'; 执行计划后发现

         以及explain select * from staffs where name <> 'July';

1.6、is null、is not null无法使用索引

        看下这个sql:explain select * from staffs where name is null; 这个就很离谱了

        explain select * from staffs where name is not null;

1.7、Like百分写最右

        like以通配符开头('%abc...')时,Mysql索引会失效变成全表扫!

        比如这个sql:explain select * from staffs where name like '%July%'; 

         如果是这样的,也是一样:explain select * from staffs where name like '%July'; 原因我这里不多解释了,上几章节都有。

        但是这样的话,就不会失效:explain select * from staffs where name like 'July%';

        因为like是范围查找,百分号在后面,Mysql会拿到字典序进行排序的方式查找对应的情况,而百分号在前面,Mysql就不知道从哪个字母开始找,于是便全表扫描。

        那这个是不是解决不了?实际面试中经常会这么问:如何解决like '%xxx%' 字符时索引不被使用的情况?

        答案是用覆盖索引避免索引失效,我们这里的索引是(name, age, pos),索引我们在查询的时候不要写select *,只要写具体的字段值,任何一个列被覆盖索引覆盖,就可以解决两边百分号的问题!!! 

        如果我这么写的话,肯定不行,因为add_time不在覆盖索引内

        总结:【like百分写最右】+【覆盖索引解决双百分问题】

1.8、字符串不加单引号索引失效

        比如这个sql:explain select * from staffs where name = 222; 索引失效

        而这个是成功走到索引的:explain select * from staffs where name = '222';

         Mysql很聪明,你以为你给我的我就查不到了,你给我的Int型的时候,实际这个字段是varchar型,传入数字会隐式的帮你转换成varchar类型,前面说过不要让Mysql做这些自动或者手动的类型转换,否则索引失效!当然查询的结果,是不会有变化的,只是sql执行上有转换。

1.9、少用or

        少用or,会导致索引失效,不是不用;

        比如explain select * from staffs where name = 'July' or name = 'z3'; 虽然都是带头大哥的name,但是索引一样丢失了。

 2、总结口诀

  • 全值匹配我最爱,最左前缀要遵守;
  • 带头大哥不能死,中间兄弟不能断;
  • 索引列上少计算,范围之后全失效;
  • Like百分写最右,覆盖索引不写星;
  • 不等空值还有or,索引失效要少用;
  • VAR引号不可丢,SQL高级并不难!

        这些口诀,可以帮助我们的记忆!

        本章讲的这一切,才是真真正正能写出高性能的JavaEE系统的实战核心关键知识点,以后写sql或多或少都会在脑子里记忆,都会自我分析,为上线前减少了很多不必要的开销,也是晋级高级或者架构师的必备之路,共勉!下一章节,索引面试总结和调优其他开发细节知识点!

以上是关于:Sql实战调优场景剖析(下)的主要内容,如果未能解决你的问题,请参考以下文章

:Sql实战调优场景剖析(上)

Mysql高级调优篇——第三章:Sql实战调优场景剖析(上)

:Sql调优在面试中深度剖析

Mysql高级调优篇——第五章:Sql调优在面试中深度剖析

干货篇:深入剖析 MySQL 索引和 SQL 调优实战

优化技术专题「系统性能调优实战」终极关注应用系统性能调优及原理剖析(下册)