• 企业400电话
  • 微网小程序
  • AI电话机器人
  • 电商代运营
  • 全 部 栏 目

    企业400电话 网络优化推广 AI电话机器人 呼叫中心 网站建设 商标✡知产 微网小程序 电商运营 彩铃•短信 增值拓展业务
    MySQL中索引失效的常见场景与规避方法

    前言

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

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

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

    MySQL索引失效的常见场景

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

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

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

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

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

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

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

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

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

    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。

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

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

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

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

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

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

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

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

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

    select * from `user` where user_id = 12;

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

    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上,因此索引失效。

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

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

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

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

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

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

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

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

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

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

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

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

    将%移到后面,如:

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

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

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

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

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

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

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

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

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

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

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

    优化索引的一些建议

    总结

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

    参考

    《为什么这些SQL语句逻辑相同,性能却差异巨大?》

    《后端程序员必备:索引失效的十大杂症》

    《58到家数据库30条军规解读》

    《MySQL的or/in/union与索引优化 | 架构师之路》

    您可能感兴趣的文章:
    • mysql索引失效的几种情况分析
    • Mysql 5.6 "隐式转换"导致的索引失效和数据不准确的问题
    • MySQL索引失效的几种情况详析
    • MySQL索引失效的几种情况汇总
    • 导致MySQL索引失效的一些常见写法总结
    • mysql的in会不会让索引失效?
    • MySQL中有哪些情况下数据库索引会失效详析
    • mysql索引失效的五种情况分析
    • Mysql索引会失效的几种情况分析
    • MySQL索引失效的典型案例
    上一篇:mysql存储过程之循环语句(WHILE,REPEAT和LOOP)用法分析
    下一篇:win10 mysql导出csv的两种方式
  • 相关文章
  • 

    © 2016-2020 巨人网络通讯 版权所有

    《增值电信业务经营许可证》 苏ICP备15040257号-8

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