服务器之家

服务器之家 > 正文

MySQL中索引失效的常见场景与规避方法

时间:2020-12-23 15:46     来源/作者:chace0120

前言

之前有看过许多类似的文章内容,提到过一些sql语句的使用不当会导致MySQL索引失效。还有一些MySQL“军规”或者规范写明了某些sql不能这么写,否则索引失效。

绝大部分的内容笔者是认可的,不过部分举例中笔者认为用词太绝对了,并没有说明其中的原由,很多人不知道为什么。所以笔者绝对再整理一遍MySQL中索引失效的常见场景,并分析其中的原由供大家参考。

当然请记住,explain是一个好习惯!

MySQL索引失效的常见场景

在验证下面的场景时,请准备足够多的数据量,因为数据量少时,MySQL的优化器有时会判定全表扫描无伤大雅,就不会命中索引了。

1. where语句中包含or时,可能会导致索引失效

使用or并不是一定会使索引失效,你需要看or左右两边的查询列是否命中相同的索引。

假设USER表中的user_id列有索引,age列没有索引。

下面这条语句其实是命中索引的(据说是新版本的MySQL才可以,如果你使用的是老版本的MySQL,可以使用explain验证下)。

?
1
select * from `user` where user_id = 1 or user_id = 2;

但是这条语句是无法命中索引的。

?
1
select * from `user` where user_id = 1 or age = 20;

假设age列也有索引的话,依然是无法命中索引的。

?
1
select * from `user` where user_id = 1 or age = 20;

因此才有建议说,尽量避免使用or语句,可以根据情况尽量使用union all或者in来代替,这两个语句的执行效率也比or好些。

2. where语句中索引列使用了负向查询,可能会导致索引失效

负向查询包括:NOT、!=、<>、!<、!>、NOT IN、NOT LIKE等。

某“军规”中说,使用负向查询一定会索引失效,笔者查了些文章,有网友对这点进行了反驳并举证。

其实负向查询并不绝对会索引失效,这要看MySQL优化器的判断,全表扫描或者走索引哪个成本低了。

3. 索引字段可以为null,使用is null或is not null时,可能会导致索引失效

其实单个索引字段,使用is null或is not null时,是可以命中索引的,但网友在举证时说两个不同索引字段用or连接时,索引就失效了,笔者认为确实索引失效,但这个锅应该由or来背,属于第一种场景~~

假设USER表中的user_id列有索引且允许null,age列有索引且允许null。

?
1
select * from `user` where user_id is not null or age is not null;

不过某些“军规”和规范中都有强调,字段要设为not null并提供默认值,是有原因值得参考的。

  • null的列使索引/索引统计/值比较都更加复杂,对MySQL来说更难优化。
  • null 这种类型MySQL内部需要进行特殊处理,增加数据库处理记录的复杂性;同等条件下,表中有较多空字段的时候,数据库的处理性能会降低很多。
  • null值需要更多的存储空,无论是表还是索引中每行中的null的列都需要额外的空间来标识。
  • 对null 的处理时候,只能采用is null或is not null,而不能采用=、in、<、<>、!=、not in这些操作符号。如:where name!='shenjian',如果存在name为null值的记录,查询结果就不会包含name为null值的记录。

4. 在索引列上使用内置函数,一定会导致索引失效

比如下面语句中索引列login_time上使用了函数,会索引失效:

?
1
select * from `user` where DATE_ADD(login_time, INTERVAL 1 DAY) = 7;

优化建议,尽量在应用程序中进行计算和转换。

其实还有网友提到的两种索引失效场景,应该都归于索引列使用了函数。

4.1 隐式类型转换导致的索引失效

比如下面语句中索引列user_id为varchar类型,不会命中索引:

?
1
select * from `user` where user_id = 12;

这是因为MySQL做了隐式类型转换,调用函数将user_id做了转换。

?
1
select * from `user` where CAST(user_id AS signed int) = 12;

4.2 隐式字符编码转换导致的索引失效

当两个表之间做关联查询时,如果两个表中关联的字段字符编码不一致的话,MySQL可能会调用CONVERT函数,将不同的字符编码进行隐式转换从而达到统一。作用到关联的字段时,就会导致索引失效。

比如下面这个语句,其中d.tradeid字符编码为utf8,而l.tradeid的字符编码为utf8mb4。因为utf8mb4是utf8的超集,所以MySQL在做转换时会用CONVERT将utf8转为utf8mb4。简单来看就是CONVERT作用到了d.tradeid上,因此索引失效。

?
1
select l.operator from tradelog l , trade_detail d where d.tradeid=l.tradeid and d.id=4;

这种情况一般有两种解决方案。

方案1: 将关联字段的字符编码统一。

方案2: 实在无法统一字符编码时,手动将CONVERT函数作用到关联时=的右侧,起到字符编码统一的目的,这里是强制将utf8mb4转为utf8,当然从超集向子集转换是有数据截断风险的。如下:

?
1
select d.* from tradelog l , trade_detail d where d.tradeid=CONVERT(l.tradeid USING utf8) and l.id=2;

5. 对索引列进行运算,一定会导致索引失效

运算如+,-,*,/等,如下:

?
1
select * from `user` where age - 1 = 10;

优化的话,要把运算放在值上,或者在应用程序中直接算好,比如:

?
1
select * from `user` where age = 10 - 1;

6. like通配符可能会导致索引失效

like查询以%开头时,会导致索引失效。解决办法有两种:

将%移到后面,如:

?
1
select * from `user` where `name` like '李%';

利用覆盖索引来命中索引。

?
1
select name from `user` where `name` like '%李%';

7. 联合索引中,where中索引列违背最左匹配原则,一定会导致索引失效

当创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则。

比如下面的语句就不会命中索引:

?
1
2
3
select * from t where k2=2;
select * from t where k3=3;
slect * from t where k2=2 and k3=3;

下面的语句只会命中索引(k1):

?
1
slect * from t where k1=1 and k3=3;

8. MySQL优化器的最终选择,不走索引

上面有提到,即使完全符合索引生效的场景,考虑到实际数据量等原因,最终是否使用索引还要看MySQL优化器的判断。当然你也可以在sql语句中写明强制走某个索引。

优化索引的一些建议

  • 禁止在更新十分频繁、区分度不高的属性上建立索引。
    • 更新会变更B+树,更新频繁的字段建立索引会大大降低数据库性能。
    • “性别”这种区分度不大的属性,建立索引是没有什么意义的,不能有效过滤数据,性能与全表扫描类似。
  • 建立组合索引,必须把区分度高的字段放在前面。

总结

以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,谢谢大家对服务器之家的支持。

原文链接:https://juejin.im/post/5df8830cf265da339565e184

标签:

相关文章

热门资讯

2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全
2020微信伤感网名听哭了 让对方看到心疼的伤感网名大全 2019-12-26
Intellij idea2020永久破解,亲测可用!!!
Intellij idea2020永久破解,亲测可用!!! 2020-07-29
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享
歪歪漫画vip账号共享2020_yy漫画免费账号密码共享 2020-04-07
电视剧《琉璃》全集在线观看 琉璃美人煞1-59集免费观看地址
电视剧《琉璃》全集在线观看 琉璃美人煞1-59集免费观看地址 2020-08-12
背刺什么意思 网络词语背刺是什么梗
背刺什么意思 网络词语背刺是什么梗 2020-05-22
返回顶部

344
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40