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

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

相关推荐
qq_52983535几秒前
Mysql中的锁
数据库·mysql·oracle
Tapdata19 分钟前
TapData 出席 TDBC 2025 可信数据库发展大会,分享“实时+信创”时代的数据基础设施演进路径
数据库
佛祖让我来巡山19 分钟前
【InnoDB优化的两大法器】内存池与后台线程解析
mysql·innodb
Little-Hu30 分钟前
QML视图组件:ListView、GridView、TableView、PathView
数据库·microsoft·qml
wmm_会飞的@鱼44 分钟前
FlexSim-汽车零部件仓库布局优化与仿真
服务器·前端·网络·数据库·数学建模·汽车
Antonio9151 小时前
【Redis】Redis 数据存储原理和结构
数据库·redis·缓存
看天走路吃雪糕1 小时前
墨者:SQL手工注入漏洞测试(MySQL数据库)
数据库·mysql·sql注入·墨者靶场
看天走路吃雪糕1 小时前
墨者:通过手工解决SQL手工注入漏洞测试(PostgreSQL数据库)
数据库·sql·postgresql·sql注入·墨者靶场
消失的旧时光-19432 小时前
Android网络框架封装 ---> Retrofit + OkHttp + 协程 + LiveData + 断点续传 + 多线程下载 + 进度框交互
android·网络·retrofit