mysql索引失效的十大问题小结

目录
  • 背景
  • 一、查询条件包含or,可能导致索引失效
  • 二、如何字段类型是字符串,where时一定用引号括起来,否则索引失效
  • 三、like通配符可能导致索引失效。
  • 四、联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。
  • 五、在索引列上使用mysql的内置函数,索引失效。
  • 六、对索引列运算(如,+、-、*、/),索引失效。
  • 七、索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。
  • 八、索引字段上使用is null, is not null,可能导致索引失效。
  • 九、左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。
  • 十、mysql估计使用全表扫描要比使用索引快,则不使用索引。
  • 总结

背景

最近生产爆出一条慢sql,原因是用了or和!=,导致索引失效。于是,总结了索引失效的十大杂症,希望对大家有帮助,加油。

一、查询条件包含or,可能导致索引失效

新建一个user表,它有一个普通索引userId,结构如下:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` int(11) NOT NULL,
  `age` int(11) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_userId` (`userId`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

执行一条查询sql,它是会走索引的,如下图所示:

把or条件+没有索引的age加上,并不会走索引,如图:

分析&结论:

  • 对于or+没有索引的age这种情况,假设它走了userId的索引,但是走到age查询条件时,它还得全表扫描,也就是需要三步过程: 全表扫描+索引扫描+合并
  • 如果它一开始就走全表扫描,直接一遍扫描就完事。
  • mysql是有优化器的,处于效率与成本,遇到or条件,索引可能失效,看起来也合情合理。

注意: 如果or条件的列都加了索引,索引可能会走的,大家可以自己试一试。

二、如何字段类型是字符串,where时一定用引号括起来,否则索引失效

假设demo表结构如下:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` varchar(32) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_userId` (`userId`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

userId为字符串类型,是B+树的普通索引,如果查询条件传了一个数字过去,它是不走索引的,如图所示:

如果给数字加上'',也就是传一个字符串呢,当然是走索引,如下图:

分析与结论:

为什么第一条语句未加单引号就不走索引了呢? 这是因为不加单引号时,是字符串跟数字的比较,它们类型不匹配,MySQL会做隐式的类型转换,把它们转换为浮点数再做比较。

三、like通配符可能导致索引失效。

并不是用了like通配符,索引一定失效,而是like查询是以%开头,才会导致索引失效。

表结构:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` varchar(32) NOT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_userId` (`userId`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

like查询以%开头,索引失效,如图:

把%放后面,发现索引还是正常走的,如下:

把%加回来,改为只查索引的字段(覆盖索引),发现还是走索引,惊不惊喜,意不意外

结论:

like查询以%开头,会导致索引失效。可以有两种方式优化:

  • 使用覆盖索引
  • 把%放后面

附: 索引包含所有满足查询需要的数据的索引,称为覆盖索引(Covering Index)。

四、联合索引,查询时的条件列不是联合索引中的第一个列,索引失效。

表结构:(有一个联合索引idx_userid_ageuserId在前,age在后)

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` int(11) NOT NULL,
  `age` int(11) DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_userid_age` (`userId`,`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

在联合索引中,查询条件满足最左匹配原则时,索引是正常生效的。请看demo:

如果条件列不是联合索引中的第一个列,索引失效,如下:

分析与结论:

  • 当我们创建一个联合索引的时候,如(k1,k2,k3),相当于创建了(k1)、(k1,k2)和(k1,k2,k3)三个索引,这就是最左匹配原则。
  • 联合索引不满足最左原则,索引一般会失效,但是这个还跟Mysql优化器有关的。

五、在索引列上使用mysql的内置函数,索引失效。

表结构:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` varchar(32) NOT NULL,
  `loginTime` datetime NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_userId` (`userId`) USING BTREE,
  KEY `idx_login_time` (`loginTime`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

虽然loginTime加了索引,但是因为使用了mysql的内置函数Date_ADD(),索引直接GG,如图:

六、对索引列运算(如,+、-、*、/),索引失效。

表结构:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` varchar(32) NOT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

虽然age加了索引,但是因为它进行运算,索引直接迷路了。。。 如图:

七、索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效。

表结构:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `userId` int(11) NOT NULL,
  `age` int(11) DEFAULT NULL,
  `name` varchar(255) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_age` (`age`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

虽然age加了索引,但是使用了!= 或者 < >,not in这些时,索引如同虚设。如下:

八、索引字段上使用is null, is not null,可能导致索引失效。

表结构:

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `card` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE,
  KEY `idx_card` (`card`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

单个name字段加上索引,并查询name为非空的语句,其实会走索引的,如下:

单个card字段加上索引,并查询name为非空的语句,其实会走索引的,如下:

但是它两用or连接起来,索引就失效了,如下:

九、左连接查询或者右连接查询查询关联的字段编码格式不一样,可能导致索引失效。

新建两个表,一个user,一个user_job

CREATE TABLE `user` (
  `id` int(11) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8mb4 DEFAULT NULL,
  `age` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=utf8;

CREATE TABLE `user_job` (
  `id` int(11) NOT NULL,
  `userId` int(11) NOT NULL,
  `job` varchar(255) DEFAULT NULL,
  `name` varchar(255) DEFAULT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_name` (`name`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

user 表的name字段编码是utf8mb4,而user_job表的name字段编码为utf8。

执行左外连接查询,user_job表还是走全表扫描,如下:

如果把它们改为name字段编码一致,还是会走索引。

十、mysql估计使用全表扫描要比使用索引快,则不使用索引。

  • 当表的索引被查询,会使用最好的索引,除非优化器使用全表扫描更有效。优化器优化成全表扫描取决与使用最好索引查出来的数据是否超过表的30%的数据。
  • 不要给'性别'等增加索引。如果某个数据列里包含了均是"0/1"或“Y/N”等值,即包含着许多重复的值,就算为它建立了索引,索引效果不会太好,还可能导致全表扫描。

Mysql出于效率与成本考虑,估算全表扫描与使用索引,哪个执行快。这跟它的优化器有关,来看一下它的逻辑架构图吧(图片来源网上)

总结

总结了索引失效的十大杂症,在这里来个首尾呼应吧,分析一下我们生产的那条慢sql。 模拟的表结构与肇事sql如下:

CREATE TABLE `user_session` (
  `user_id` varchar(32) CHARACTER SET utf8mb4 NOT NULL,
  `device_id` varchar(64) NOT NULL,
  `status` varchar(2) NOT NULL,
  `create_time` datetime NOT NULL,
  `update_time` datetime DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
  PRIMARY KEY (`user_id`,`device_id`) USING BTREE
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
explain
update user_session set status =1
where  (`user_id` = '1' and `device_id`!='2')
or (`user_id` != '1' and `device_id`='2')

分析:

  • 执行的sql,使用了or条件,因为组合主键(user_id,device_id),看起来像是每一列都加了索引,索引会生效。
  • 但是出现!=,可能导致索引失效。也就是or+!=两大综合症,导致了慢更新sql。

解决方案:

那么,怎么解决呢?我们是把or条件拆掉,分成两条执行。同时给device_id加一个普通索引。

最后,总结了索引失效的十大杂症,希望大家在工作学习中,参考这十大杂症,多点结合执行计划expain和场景,具体分析 ,而不是按部就班,墨守成规,认定哪个情景一定索引失效。

到此这篇关于mysql索引失效的十大问题的文章就介绍到这了,更多相关mysql索引失效的十大问题内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • mysql回表致索引失效案例讲解

    简介 mysql的innodb引擎查询记录时在无法使用索引覆盖的场景下,需要做回表操作获取记录的所需字段. mysql执行sql前会执行sql优化.索引选择等操作,mysql会预估各个索引所需要的查询代价以及不走索引所需要的查询代价,从中选择一个mysql认为代价最小的方式进行sql查询操作.而在回表数据量比较大时,经常会出现mysql对回表操作查询代价预估代价过大而导致索引使用错误的情况. 案例 示例如下,在5.6版本的mysql.1CPU2G内存的Linux环境下,新建一个测试表,并创建将近

  • MySQL索引失效的几种情况汇总

    一.索引不存储null值 更准确的说,单列索引不存储null值,复合索引不存储全为null的值.索引不能存储Null,所以对这列采用is null条件时,因为索引上根本 没Null值,不能利用到索引,只能全表扫描. 为什么索引列不能存Null值? 将索引列值进行建树,其中必然涉及到诸多的比较操作.Null值的特殊性就在于参与的运算大多取值为null. 这样的话,null值实际上是不能参与进建索引的过程.也就是说,null值不会像其他取值一样出现在索引树的叶子节点上. 二.不适合键值较少的列(重复

  • MySQL索引失效的典型案例

    典型案例 有两张表,表结构如下: CREATE TABLE `student_info` (   `id` int(11) NOT NULL,   `name` varchar(10) DEFAULT NULL,   PRIMARY KEY (`id`),   KEY `idx_name` (`name`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 CREATE TABLE `student_score` (   `id` int(11) NOT NULL,

  • mysql索引失效的五种情况分析

    索引并不是时时都会生效的,比如以下几种情况,将导致索引失效: 如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引 2.对于多列索引,不是使用的第一部分,则不会使用索引 3.like查询是以%开头 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引 5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引 此外,查看索引的使用情况 show status lik

  • 解决mysql模糊查询索引失效问题的几种方法

    我们在使用like %通配符时常常会引起索引失效的问题. 这里,我们讨论一下like使用%的几种情况: 下列例子用到的索引(VC_STUDENT_NAME) 一.like 'xx%' EXPLAIN select * from t_student where VC_STUDENT_NAME like '王%' 我们发现使用%不放在开头的时候,索引是有效的 二.like '%xx' EXPLAIN select * from t_student where VC_STUDENT_NAME like

  • Mysql索引会失效的几种情况分析

    索引并不是时时都会生效的,比如以下几种情况,将导致索引失效: 1.如果条件中有or,即使其中有条件带索引也不会使用(这也是为什么尽量少用or的原因) 注意:要想使用or,又想让索引生效,只能将or条件中的每个列都加上索引 2.对于多列索引,不是使用的第一部分,则不会使用索引 3.like查询是以%开头 4.如果列类型是字符串,那一定要在条件中将数据使用引号引用起来,否则不使用索引 5.如果mysql估计使用全表扫描要比使用索引快,则不使用索引 此外,查看索引的使用情况show status li

  • mysql索引失效的几种情况分析

    1.最佳左前缀原则--如果索引了多列,要遵守最左前缀原则.指的是查询要从索引的最左前列开始并且不跳过索引中的列. 前提条件:表中已添加复合索引(username,password,age) 分析:该查询缺少username,查询条件复合索引最左侧username缺少,违反了最佳左前缀原则,导致索引失效,变为ALL,全表扫描 分析:查询条件缺少username,password,查询条件复合索引最左侧username,password缺少,违反了最佳左前缀原则,导致索引失效,变为ALL,全表扫描

  • MySQL索引失效的几种情况详析

    1.前导模糊查询不能利用索引(like '%XX'或者like '%XX%') 假如有这样一列code的值为'AAA','AAB','BAA','BAB' ,如果where code like '%AB'条件,由于前面是 模糊的,所以不能利用索引的顺序,必须一个个去找,看是否满足条件.这样会导致全索引扫描或者全表扫 描.如果是这样的条件where code like 'A % ',就可以查找CODE中A开头的CODE的位置,当碰到B开头的 数据时,就可以停止查找了,因为后面的数据一定不满足要求.

  • mysql索引失效的十大问题小结

    目录 背景 一.查询条件包含or,可能导致索引失效 二.如何字段类型是字符串,where时一定用引号括起来,否则索引失效 三.like通配符可能导致索引失效. 四.联合索引,查询时的条件列不是联合索引中的第一个列,索引失效. 五.在索引列上使用mysql的内置函数,索引失效. 六.对索引列运算(如,+.-.*./),索引失效. 七.索引字段上使用(!= 或者 < >,not in)时,可能会导致索引失效. 八.索引字段上使用is null, is not null,可能导致索引失效. 九.左连

  • 分享15个Mysql索引失效的场景

    目录 背景 数据库及索引准备 创建表结构 初始化数据 数据库版本及执行计划 1 联合索引不满足最左匹配原则 2 使用了select * 3 索引列参与运算 4 索引列参使用了函数 5 错误的Like使用 6 类型隐式转换 7.使用OR操作 8 两列做比较 9 不等于比较 10 is not null 11 not in和not exists 12 order by导致索引失效 13 参数不同导致索引失效 14 其他 小结 背景 无论你是技术大佬,还是刚入行的小白,时不时都会踩到Mysql数据库不

  • 导致MySQL索引失效的一些常见写法总结

    前言 最近一直忙着处理原来老项目遗留的一些SQL优化问题,由于当初表的设计以及字段设计的问题,随着业务的增长,出现了大量的慢SQL,导致MySQL的CPU资源飙升,基于此,给大家简单分享下这些比较使用的易于学习和使用的经验. 这次的话简单说下如何防止你的索引失效. 再说之前我先根据我最近的经验说下我对索引的看法,我觉得并不是所以的表都需要去建立索引,对于一些业务数据,可能量比较大了,查询数据已经有了一点压力,那么最简单.快速的办法就是建立合适的索引,但是有些业务可能表里就没多少数据,或者表的使用

  • MySQL索引失效原理

    目录 1.索引失效原因 2.再来看看哪些情况会破坏索引的有序性. - 对索引字段做函数操作 - 隐式类型转换 - 隐式字符编码转换 3.总结 1.索引失效原因 首先看看哪些情况下,将会导致查找不能利用索引的有序性. 假设一个表test中有a,b,c,d四个字段,c是主键. 在a,b字段上建立联合索引(a,b):CREATE index idx_a_b on test(a,b); B+树联合索引.JPG 可以得到的规律是:优先按a字段从小到大排序,a字段相等的按b字段从小到大排序: 分析以下情况,

  • MySQL索引失效之隐式转换的问题

    目录 常见索引失效: 一.常见索引失效场景 1.条件字段函数操作 2.条件字段运算操作 3.隐式类型转换 4.隐式字符编码转换 二.类型转换 1.字符串转整型 2.时间类型转换 常见索引失效: 1. 条件索引字段"不干净":函数操作.运算操作 2. 隐式类型转换:字符串转数值:其他类型转换 3. 隐式字符编码转换:按字符编码数据长度大的方向转换,避免数据截取 一.常见索引失效场景 root@test 10:50 > show create table t_num\G ******

  • mysql索引失效的常见九种原因图文详解

    目录 前言: 1.最佳左前缀法则 3.计算.函数.类型转换(自动或手动)导致索引失效 4.范围条件右边的列索引失效 5.不等于(!= 或者<>)导致索引失效 6.is null可以使用索引,is not null无法使用索引 7.like以通配符%开头索引失效 8.OR 前后只要存在非索引的列,都会导致索引失效 9.数据库和表的字符集统一使用utf8mb4 总结 前言: MySQL中提高性能的一个最有效的方式是对数据表设计合理的索引.索引提供了高效访问数据的方法,并且加快查询的速度, 因此索引

随机推荐