前言

你是否有过这样的困惑?
啊~!索引怎么失效了? 为什么规定sql不能那样写?我这个sql没问题啊,为啥老大批斗我?

的确在我们的日常开发中,一些sql语句的使用不当就会导致MYSQL的索引失效,因此大家可能去去检索了一些类似索引为何失效的文章。

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

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并提供默认值,是有原因值得参考的。

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

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

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

    优化建议,尽量在应用程序中进行计算和转换。
    其实还有网友提到的两种索引失效场景,应该都归于索引列使用了函数。

    隐式类型转换导致的索引失效
    比如下面语句中索引列user_id为varchar类型,不会命中索引:

    select * from `user` where user_id = 12;
    

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

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

    隐式字符编码转换导致的索引失效
    当两个表之间做关联查询时,如果两个表中关联的字段字符编码不一致的话,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;
    
  2. 对索引列进行运算,一定会导致索引失效
    运算如+,-,*,/等,如下:

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

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

    select * from `user` where age = 10 - 1;
  3. like通配符可能会导致索引失效
    like查询以%开头时,会导致索引失效。解决办法有两种:

    将%移到后面,如:

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

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

    select name from `user` where `name` like '%李%';
    
  4. 联合索引中,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;
    
  5. MySQL优化器的最终选择,不走索引
    上面有提到,即使完全符合索引生效的场景,考虑到实际数据量等原因,最终是否使用索引还要看MySQL优化器的判断。当然你也可以在sql语句中写明强制走某个索引。

优化索引的一些建议

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

常威,你还说你不会索引优化?MYSQL索引失效的常见场景与规避方法相关推荐

  1. 常见面试题:为什么MySQL索引要用B+Tree呢?(看完你就能和面试官笑谈人生了)

    title: 常见面试题:为什么MySQL索引要用B+Tree呢?(看完你就能和面试官笑谈人生了) tags: 面试常见题 常见面试题:为什么MySQL索引要用B+Tree呢?(看完你就能和面试官笑谈 ...

  2. 分度值1g是什么意思_都什么年代了,还有人在 MySQL 索引上碰一鼻子灰?

    推荐阅读: 阿里技术大牛的Spring秘籍:Spring+SpringBoot+源码解析+SpringCloud​zhuanlan.zhihu.com 不愧是阿里架构师,一份文档就把 Docker 讲 ...

  3. mysql索引三个字段查询两个字段_mysql中关于关联索引的问题——对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引?...

    情况描述:在MySQL的user表中,对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引? 根据查询字段的位置不同来决定,如查询a,     a,b    a,b, ...

  4. mysql索引三个字段查询两个字段_mysql中关于关联索引的问题——对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引?......

    情况描述:在MySQL的user表中,对a,b,c三个字段建立联合索引,那么查询时使用其中的2个作为查询条件,是否还会走索引? 根据查询字段的位置不同来决定,如查询a,     a,b    a,b, ...

  5. 普通索引和唯一索引,难道还分不清

    本文分享自华为云社区<MySQL的普通索引和唯一索引到底什么区别?>,作者: JavaEdge. 1 概念区分 普通索引 V.S 唯一索引 普通索引可重复,唯一索引和主键一样不能重复. 唯 ...

  6. 你以为你懂MySQL索引?阿里的面试官:你还太嫩!

    相信很多人对于MySQL的索引都不陌生,索引(Index)是帮助MySQL高效获取数据的数据结构. 因为索引是MySQL中比较重点的知识,相信很多人都有一定的了解,尤其是在面试中出现的频率特别高.楼主 ...

  7. 面试官常问:为什么 MongoDB 索引选择B-树,而 Mysql 索引选择B+树(精干总结)

    一.B树和B+树的区别 很明显,我们想向弄清楚原因就要知道B树和B+树的区别.为了不长篇大论.我们直接给出他们的形式总结他们的特点. 1.B树 B树是一种自平衡的搜索树,形式很简单: 这就是一颗B-树 ...

  8. MySQL索引及常见面试题

    一.索引是什么? 索引(Index)是帮助 MySQL 高效获取数据的数据结构,是对表中一列或多列值进行排序的结构. 就比如索引是一本书的目录,可以通过目录快速查找自己想要查询的东西. 二.索引为什么 ...

  9. bugfree 检测不到mysql_安装bugfree时,提示Mysql未安装,找了网上的方法更改代码,但是还没有得到解决,怎么办?...

    源自:- 安装bugfree时,提示Mysql未安装,找了网上的方法更改代码,但是还没有得到解决,怎么办? 问题出现在bugfree安装检查环境时,一直提示未安装mysql. 小编百思不得其姐,  最 ...

  10. “约见”面试官系列之常见面试题之第九十一篇之简述Vue的生命周期适用于哪些场景(建议收藏)

    答:beforeCreate:在new一个vue实例后,只有一些默认的生命周期钩子和默认事件,其他的东西都还没创建.在beforeCreate生命周期执行的时候,data和methods中的数据都还没 ...

最新文章

  1. NanoPi NEO Air使用十一:编写SPI驱动点亮TFT屏幕,ST7789V
  2. SpringCloud + Docker
  3. python学习的一个定位_python学习之——selenium元素定位
  4. c语言case接收字符,switch-case-break:字符释义 | 新思维:C语言程序设计
  5. H264 帧边界识别简介
  6. 简述HTML5的概念和组成结构,JS相关概念
  7. 多次为 selecttemp 指定了列 bstkd_36列城市铁路列车、轨道诊断、货车大修、调车机车备品备件招标...
  8. 计算机地图制图算法与原理重点,计算机地图制图算法.pptx
  9. 3DEC/PFC离散元入门篇
  10. 中控考勤仪IFace302多线程操作时无法订阅事件
  11. 读 《我为什么要逃离北上广了?》有感
  12. CAT的Client端初始化
  13. 【JPress】Template
  14. java如何为图片加水印
  15. DHCP spoofing,DHCP snooping,傻傻分不清
  16. 李银河应该重读王小波《摆脱童稚状态》--尼伯龙根·蜗藤
  17. 中国大学生计算机设计大赛英语,中国大学生计算机设计大赛2018.pdf
  18. 线性回归模型和非线性回归模型的区别是
  19. Bentley OpenFlows FLOOD 集成的洪水模拟软件
  20. HMAC-SHA1签名认证算法

热门文章

  1. matlab图像自动标注程序,基于MATLAB/GUI的图像语义自动标注系统
  2. 使用python embedded distribution
  3. 服务器和网站域名,网站服务器和域名的区别
  4. 手机用html电视,4种方法教你手机如何连接智能电视
  5. java编程:对两个分数进行简单的算术运算
  6. ENVI处理高分一号PMS影像数据
  7. 运用supervisor管理thinkqueue 和swoole推送
  8. 前端程序员兼职?不妨来试试这几个方法
  9. 前端项目emoji表情包的使用
  10. IDEA编辑页面html jsp js java无法即时生效