被leader怼对MySQL索引一无所知,我再次打了leader的脸,最后leader不讲武德,我没闪大意了

625 阅读6分钟

经过第一天的成功给leader的问题都给回答上来,我的leader苦思冥想一晚上,各种百度,各种搜,各种查,终于搜到了法子对付我。。。害,我估计我离走是不远了

leader:小伙子啊,看你昨天回答的不错,今天我还有一些问题,看你能不能回答出来了

我:老大,我错了。我真的不会别的了!

leader:别别别,您这么牛,怎么能不会呢,废话少说,如果回答不出来,哼哼,你耗子尾汁

leader:听着,你昨天不是说出来覆盖索引了么,那我今天在问问你,最左前缀法则是什么你知道么?

我:(心里:就这?看来昨天老大这百度没百度出来啥东西呀)我想想奥。。。

最左前缀法则

说到最左前缀法则,就要想到复合索引,复合索引是遵循最左前缀法则的,顾名思义,就是查询的时候以最左侧的为例开始使用索引,如果从索引的第二列或者之后开始,索引就会失效

这里select name,age,sex from user where name='xx'这个是走索引的 where name='xx' and age='22' 这个也是走索引的,where name='xx' and age='22' and sex='男'这个也是走索引的

select name,age,sex from user where age='22' 这个是不走索引的,where age='22' and sex='男' 这个也是不走索引的,where name='xx' and sex='男'这个是走索引的,但是只会走name,走不到sex,所以,大家明白了,他这个是完全从左到右的来执行,如果中间断了,那么断之前是走索引的,断之后是不走的,如果不是从最左开始判断,那就都不会走索引。

leader:别得意,这只是前戏,那你说一下,如何sql优化吧。记得要说全点,不然你走着瞧。

我:。。。。这。。。。。

SQL优化

提到SQL优化,一定就得提到一个工具,Explain,它可以对select语句进行分析,并输出select执行的详细信息,供开发人员有针对性的优化

还是昨天的语句

EXPLAIN select userID,userName,age from user where userName='张三'

简单来分析一下:

select_type:表示查询的类型,常用的值如下:

  • SIMPLE:表示查询语句不包含子查询或union
  • PRIMARY:表示此查询是最外层查询
  • UNION:表示此查询是UNION的第二个或后续的查询
  • DEPENDENT UNION:UNION中的第二个或后续的查询语句,使用了外面查询结果
  • UNION RESULT:UNION的结果
  • SUBQUERY:SELECT子查询语句
  • DEPENDENT SUBQUERY:SELECT子查询语句依赖外层查询的结果 最常见的查询类型就是SIMPLE,表示我们的查询没有子查询也没有用到UNION查询

table:这一列表示正在访问哪个表。

type:表示存储引擎查询数据时采用的方式。比较重要的一个属性,通过它可以判断出查询是全表扫描还是基于索引的部分扫描。常用的属性值如下,从上至下效率依次增强。

  • ALL:表示全表扫描,性能最差
  • index:表示基于索引的全表扫描,先扫描索引再扫描全表数据
  • range:表示使用索引范围查询。使用>,>=,<,<=,in等等
  • ref:表示使用非唯一索引进行单值查询
  • eq_ref:一般情况下出现在多表join查询,表示前面表的每一个记录,都只能匹配后面表的一行结果。
  • const:表示使用主键或唯一索引做等值查询,常量查询
  • null:表示不用访问表,速度最快。

possible_keys:表示查询时能够使用到的索引。注意并不一定会真正使用,显示的是索引名称

key:表示查询时真正使用到的索引,显示的是索引名称

rows:MySQL查询优化器会根据统计信息,估算SQL要查询到结果需要扫描多少行记录,原则上rows是越少效率越高,可以直观的了解到SQL效率高低

key_len:表示查询使用了索引的字节数量。可以判断是否全部使用了组合索引 key_len的计算规则如下:

  • 字符串类型
    • 字符串长度跟字符集有关:latin1=1,gbk=2,utf8=3,utf8mb4=4; char(n):n字符集长度 varchar(n):n字符集长度+2字节
  • 数值类型
    • TINYINT:1个字节
    • SMALLINT:2个字节
    • MEDIUMINT:3个字节
    • INT,FLOAT:4个字节
    • BIGINT,DOUBLE:8个字节
  • 时间类型
    • DATE:3个字节
    • TIMESTAMP:4个字节
    • DATETIME:8个字节
  • 字段属性
    • Null属性占用1个字节,如果一个字段设置了Not NULL,则没有此项

ref:这一列显示了在key列记录的索引中,表查找值所用到的列或常量,常见的有:const(常量),字段名(列:film.id)

Extra

Extra表示很多额外的信息,各种操作会在Extra提示相关信息,常见几种如下:

  • Using where
    • 表示查询需要通过索引回表查询数据
  • Using index
    • 表示查询需要通过索引,索引就可以满足所需数据
  • Using filesort
    • 表示查询出来的结果需要额外的排序,数据量小在内存,大的话在磁盘,因此有Using filesort建议优化
  • Using temprorary
    • 查询使用到了临时表,一般出现于去重,分组等操作。

说到这里,相信大家做查询的时候,一定是不少使用like进行模糊查询吧?那用like查询的时候,索引能起到作用么?

MySQL在使用like模糊查询时,索引是可以被使用的,只有把%字符写在后面才会使用到索引。 还是之前的表

ALTER TABLE user ADD INDEX u (userName,age);
EXPLAIN select userID,userName,age from user where userName like'%三%'

首先看一下type类型是ALL,说明是全表扫描,在看一下key是空,说明没有用到索引

EXPLAIN select userID,userName,age from user where userName like '三%'

大家看到区别了吧,这里是走到了u这个索引,而这个u的索引是上面创建的userName和age的组合索引。

这里聪明的人应该就想到了,为什么%写前面不走索引,上一篇也说了MySQL索引底层是B+树,前面写百分号就相当于要把所有的都搜索一遍,肯定不走索引呀,对吧。

在MySQL中,查询支持filesort和index两种方式的排序,filesort是先把结果查出,然后在缓存或者磁盘进行排序操作,效率低。使用index是指利用索引自动实现排序,不需另做排序操作,效率会比较高。

filesort有两种排序算法:双路排序和单路排序

双路排序:需要两次磁盘扫描读取,最终得到用户数据。第一次将排序字段读取出来,然后排序;第二次去读取其他字段数据。

单路排序:从磁盘查询所需的列数据,然后在内存排序将结果返回。如果查询数据超出缓存sort_buffer,会导致多次磁盘读取操作,并创建临时表,最后产生了多次IO,反而会增加负担。解决方案:少使用select * ;增加sort_buffer_size容量和max_length_for_sort_data容量

如果我们Explain分析SQL,结果中Extra属性显示Using filesort,表示使用了filesort排序方式,需要优化。如果Extra属性显示Using index时,表示覆盖索引,也表示所有操作在索引上完成,也可以使用index排序方式,建议大家尽可能采用覆盖索引。

以下几种情况,会使用index方式的排序

  • Order by子句索引列组合满足索引最左前列
explain select id from user order by id; //对应(id)、(id,name)索引有效
  • WHERE子句+ORDER BY子句索引列组合满足索引最左前列
explain select id from user where age=18 order by name; //对应 (age,name)索引

以下几种情况,会使用filesort方式的排序

  • 对索引列同时使用ASC和DESC
explain select id from user order by age asc,name desc; //对应 (age,name)索引
  • WHERE子句和ORDER BY子句满足最左前缀,但where子句使用了范围查询(例如>、<、in 等)
explain select id from user where age>10 order by name; //对应 (age,name)索引
  • ORDER BY或者WHERE+ORDER BY索引列没有满足索引最左前列
explain select id from user order by name; //对应(age,name)索引
  • 使用了不同的索引,MySQL每次只采用一个索引,ORDER BY涉及了两个索引
explain select id from user order by name,age; //对应(name)、(age)两个索 引
  • WHERE子句与ORDER BY子句,使用了不同的索引
explain select id from user where name='tom' order by age; //对应 (name)、(age)索引
  • WHERE子句或者ORDER BY子句中索引列使用了表达式,包括函数表达式
explain select id from user order by abs(age); //对应(age)索引

下面再给大家举例几种常见的不走索引的情况:

  1. 在索引列上做任何操作(计算,函数,(自动or手动)类型转换),会导致索引失效
  2. 使用!=或者<>,not in,not exists的时候无法使用索,<小于,>大于,<=,>=这些,mysql内部优化器会根据索引比例,表大小等多个因素整体评估是否使用索引
  3. is null,is not null一般情况下也无法使用索引
  4. 字符串不加单引号索引失效(这里是很坑的,之前有一次我们项目上也出现了这个小失误,有人写sql字符串上没加单引号,就比如phone设置的varchar类型,但是查询的时候,直接phone=123这样查询,而不是phone='123'这样查询,由于数据量非常的大,导致整个集群压力都很大,服务卡死。排查了半天才发现这个问题,这也是大部分人很容易马虎的地方
  5. 少用or或in,用它查询的时候,mysql不一定使用索引,mysql内部优化器会根据检索比例,表大小等多个因素整体评估是否使用索引。
  6. 如果是范围查询,范围比较大的情况下,可能也是不走索引的,建议把范围拆开,拆成两个小范围这样,是可能会走索引的

之前还有一次我们在查询比较慢的接口的时候,最后找到sql的问题,因为这个表一开始压测的时候是没什么问题的,因为数据量比较小,但是数据量大起来的时候,就发现问题了,全是无效索引的。就很难受

其实数据库优化,也不仅仅是在sql层面,硬件啊,表结构设计啊,商业需求都有关系,有些时候,sql再优化,还是会很慢,那这个就可以看看这个需求是否合理了,就比如,你几十亿的数据,人家就要全都看,各种条件查询,那你觉得这个合理么,我觉得需求层面就不太合理,大家在写代码的时候,不要只是为了完成业务,更多的是要思考性能方面的问题,业务不管是简单还是难,只能说对你的提升是有帮助,但是如果想去好的互联网公司,性能优化这里是必须的,还有系统的整体架构都要摸清楚。这才是最锻炼人的。

leader:行啊,小伙子,最后来这么长句感言,不过你说的还是不太细啊,这具体的sql优化

我:老大啊,我这要说细了,我不得说一天啊,如果这个文章点赞可以破五十,我之后单门出一篇文章具体实战一下sql优化!

leader:这是你说的,小伙子,今天我还是没压住你,我再想想mysql还有什么题能考考你奥。。。

我:。。。。。

leader:有了!那你简单来给我说说mysql的事务和锁这块吧?

我:。。。。老年人你是真不讲武德,我大意了,没用闪。。。我这里还没学呢!不是说索引这里么!!!

我:。。。溜了溜了。回家好好看看这里下次再来回答你!