mysql索引失效

一、哪些情况会导致索引失效

  1. 创建了组合索引,但查询条件未遵守最左匹配原则;
  2. 在索引列上进行计算、函数、类型转换等操作;
  3. 发生隐式转换
  4. 没覆盖索引时,以 % 开头的LIKE 查询比如 LIKE '%abc';
  5. 查询条件中使用 OR,且 OR的前后条件中有一个列没有索引,涉及的索引都不会被使用到(MySQL里,即使or左边条件满足,右边条件依然要进行判断)
  6. IN的取值范围较大时会导致索引失效,走全表扫描(NOT IN 和 IN 的失效场景相同);
  7. 范围条件右边的列索引失效,例如(a,b,c)联合索引,查询条件a,b,c,如果b使用了范围查询,那么b右边的c索引失效
  8. 不同的字符集进行比较前需要进行转换会造成索引失效。
  9. ...

二、分析原因

挑几个好玩的条件进行分析:

2.1、为什么联合索引的使用要遵循最佳左前缀?

举例:创建一个(a,b)的联合索引,那么它的索引树就是下图的样子

直接看图中第二排:可以看到a的值是有顺序的,1,1,2,2,3,3,而b的值是没有顺序的1,2,1,4,1,2。

但是我们又可发现a在等值的情况下,b值又是按顺序排列的,但是这种顺序是相对的。

这是因为MySQL创建联合索引的规则是首先会对联合索引的最左边第一个字段排序,在第一个字段的排序基础上,然后在对第二个字段进行排序。所以b=2这种查询条件没有办法利用索引。

2.2、类型转化为什么会导致索引失效?发生类型转化会一定导致索引失效吗?

sql 复制代码
CREATE TABLE `test1` (
    `id` int(11) NOT NULL,
    `num1` int(11) NOT NULL DEFAULT '0',
    `num2` varchar(11) NOT NULL DEFAULT '',
    PRIMARY KEY (`id`),
    KEY `num1` (`num1`),
    KEY `num2` (`num2`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;

1: SELECT * FROM test1 WHERE num1 = 10000; ✅

2: SELECT * FROM test1 WHERE num1 = '10000';✅

3: SELECT* FROM test1 WHERE num2 = 10000;❌

4: SELECT * FROM test1 WHERE num2 = '10000';✅

经测试这四条 SQL 最后的执行结果却相差很大,在千万级的数据量下,其中 124 三条 SQL 基本都是瞬间出结果,大概在 0.001~0.005 秒,这样的结果可以判定这三条 SQL 性能基本没差别了。

但是第三条 SQL,多次测试耗时基本在 4.5~4.8 秒之间。

这四条sql的执行计划如下:

问题点来了:为什么2和3左右两边的类型不一致,2可以用索引,3却不能用索引?

请看MYSQL官方文档:https://dev.mysql.com/doc/refman/5.7/en/type-conversion.html?spm=5176.100239.blogcont47339.5.1FTben

切换成中文模式,我截了一个比较转化规则的图片:

根据官方文档的描述,我们的第 23 两条 SQL 都发生了隐式转换,第 2 条 SQL 的查询条件num1 = '10000',左边是int类型右边是字符串,第 3 条 SQL 相反,那么根据官方转换规则第 7 条,左右两边都会转换为浮点数再进行比较。

第 3 条 SQL:SELECT * FROM test1 WHERE num2 = 10000; 右边int类型10000转换浮是唯一的。但是左边是检索条件,'10000'转到10000是唯一,但是其他字符串也可以转换为10000,比如'10000a','010000','10000'等等都能转为浮点数10000,这样的情况下,是不能用到索引的。

tips:

  1. 不以数字开头的字符串都将转换为0。如'abc'、'a123bc'、'abc123'都会转化为0;
  2. 以数字开头的字符串转换时会进行截取,从第一个字符截取到第一个非数字内容为止。比如'123abc'会转换为123,'012abc'会转换为012也就是12,'5.3a66b78c'会转换为5.3,其他同理。

所以:SELECT* FROM test1 WHERE num2 = 10000; 这个 SQL 可以查出 num2 = '10000a','010000'这些数据

而第2条SQL,字符串'10000'转化为浮点数10000是固定的,根据这个条件去检索索引是ok的

2.3、 IN 的取值范围较大时会导致索引失效(我认为了解即可,主要和mysql优化器判断使用索引的成本有关)

其底层原理主要涉及查询优化器的决策过程和索引的使用成本。

下面是我问GPT,他给出的回答:

  1. 查询优化器的工作原理: MySQL 的查询优化器会根据查询条件生成多种执行计划,并选择其中成本最低的执行计划。成本的计算包括 I/O操作、CPU 消耗等。
  2. 索引的使用成本: 当 IN的取值范围较小时,查询优化器会认为使用索引进行多次查找的成本较低,因此会选择使用索引。然而,当 IN的取值范围较大时,查询优化器会认为使用索引进行多次查找的成本较高,因为每次查找都需要进行随机 I/O 操作,导致整体成本增加。
  3. 全表扫描的选择: 当 IN的取值范围较大时,查询优化器会认为全表扫描的成本较低,因为全表扫描只需要一次顺序读取所有数据,而使用索引则需要进行多次随机读取数据。顺序读取的I/O 操作通常比随机读取的 I/O 操作更快,因此在这种情况下,查询优化器会选择全表扫描。

下面是mysql官方文档的解释:(直接看第三条)

相关链接:https://dev.mysql.com/doc/refman/5.7/en/table-scan-avoidance.html

这张图也说明了其他索引可能失效的情况,比如表的数据太少了等等...

相关推荐
橙子1991101644 分钟前
Kotlin 中的 Object
android·开发语言·kotlin
layman05282 小时前
openEuler 虚拟机中 Shell 脚本实现自动化备份与清理实践
数据库·oracle·自动化
AD钙奶-lalala5 小时前
android:foregroundServiceType详解
android
大胃粥8 小时前
Android V app 冷启动(13) 焦点窗口更新
android
奔跑的小十一8 小时前
JDBC接口开发指南
java·数据库
熊大如如9 小时前
PostgreSQL 创建只读账户
数据库·postgresql
Zzzone6839 小时前
MySQL故障排查与生产环境优化
mysql
尽兴-10 小时前
达梦数据库CASE WHEN条件
数据库·oracle·达梦
遗忘妳10 小时前
PostgreSQL初体验
数据库·postgresql
苹果醋311 小时前
AI大模型竞赛升温:百度发布文心大模型4.5和X1
java·运维·spring boot·mysql·nginx