MySQL索引失效的几种常见场景详解
作者:努力学习java的哈吉米大王
我们在学习的过程中常能听到人们谈论到MySQL的索引失效了。那么为什么🧐索引会失效呢?
一、为什么索引会失效
我们需要知道索引的本质是以空间换时间的一种结构,“排好序的数据结构(例如InnoDB+树)”,能够帮助数据库快速定位数据。但是如果查询条件破坏了索引的有序性或者查询优化器判断“全表扫描比走索引更快”,就会放弃使用索引,导致索引失效。
接下来我们来以一些具体的场景来看!
二、索引失效的场景
2.1 对索引字段做“函数/运算操作”,破坏索引的有序性
索引存储的是字段原始值,一旦对字段做函数处理(如SUBSTR(),DATE())或者运算(如+,-),数据库无法直接使用索引定位,只能全表扫描。
-- 对索引字段name做函数处理,索引失效 SELECT * FROM user WHERE SUBSTR(name,1,3)='哈基米'; -- 对索引字段age做运算,索引失效 SELECT * FROM user WHERE age+3=24;
对于第二个SQL进行优化:把函数/运算移到等号右边
-- 以下两种都会走索引 SELECT * FROM user WHERE age=21; SELECT * FROM user WHERE age=24-3;
2.2 隐式类型转换,导致索引字段被“隐式处理”
当查询条件中,字段类型与传入值类型不匹配时,MySQL会自动做类型转化(相当于隐式函数操作),导致索引失效
-- age是INT类型,传入字符串'21',会被转为INT(相当于CAST(age as CHAR)) SELECT * FROM user WHERE age='21'; -- 索引失效
对于这个SQL进行优化:保证传入的参数类型与字段类型相同
SELECT * FROM user WHERE age=21
2.3 LIKE查询以%开头,无法利用索引有序性
B+树索引是按照字段前缀排序的,LIKE '%XXX'表示“后缀匹配”,无法通过索引的有序性定位,只能全表扫描,而LIKE 'XXX%'(前缀匹配)可以走索引。
-- %在开头,索引失效 SELECT * FROM user WHERE name LIKE '%基米';
对于该SQL进行优化:不使用后缀匹配,如果业务必需后缀匹配,可以考虑“倒序存储+前缀索引”(如存name_reverse='米基哈',查询LIKE '米%')
2.4 组合索引不满足“最左前缀原则”
组合索引(a,b,c)的B+树是按照a->b->c的顺序排序的,查询条件必需包含最左列(a),否则不误利用索引。
-- 组合索引(a,b,c),缺少最左列a,索引失效 SELECT * FROM table1 WHERE b=2 AND c=2; -- 虽然有a,但是中间列b缺失,只能用到a的索引,b和c无法利用 SELECT * FROM table1 WHERE a=2 AND c=2;
对上述的SQL进行优化:按最左前缀原则设计查询条件,或调整组合索引顺序(将高频字段放左边)
2.5 OR连接的条件中,存在未建索引的字段
OR的逻辑是“满足任意一个条件即可”,如果其中一个字段没索引,数据库无法通过索引快速定位所有满足条件的行(会查询到不满足非索引条件的行),只能放弃索引走全表扫描。
-- age有索引,name无索引,OR导致age索引失效 SELECT * FROM user WHERE age=21 OR name='哈基米';
对上述SQL进行优化:给OR连接的所有字段都建立索引,或改用UNION拆分查询:
SELECT * FROM user WHERE age=21 UNION SELECT * FROM user WHERE name='哈基米'; -- 分别走各自的索引
注意📢:
假设字段age和name都有自己的索引
执行:SELECT * FROM user WHERE age=21 OR name='哈基米' ;
即使age和name分别有单独的索引,这个查询大概率不会走任何索引,会进行全表扫描
原因:
OR的逻辑是“满足任意一个条件即可”,而数据库的索引是单个字段排序的:
- age索引只能快速定位age=21的行;
- name索引只能快速定位到name='哈基米'的行;
- 数据库无法通过一个索引同事定位两个条件的结果,若分别使用两个索引再合并结果,开销可能比全表扫描更大(尤其是当两个条件的结果集都比较大时)
执行:SELECT * FROM user WHERE age=21 AND name='哈基米';
假设name和age都只有单独的索引,没有两者的组合索引时,数据库会选择其中一个过滤效果更好的索引(例如age=21能筛选出更少的行,则优先用age索引),定位后再在结果中过滤name='哈基米'的行。
2.6 查询优化器判断“全表扫描更快”
当数据量很少(例如几百行),或查询结果占表数据的大部分(如WHERE age=21返回90%的数据),查询优化器会认为“全表扫描比走索引更快”(索引也需要IO开销),此时会主动放弃索引。这种是“合理失效”,无效优化,数据库会自动选择最优方案。
2.7 其他场景
- NOT IN/<>''!=:这些操作可能导致索引失效(视版本和数据分布而定),建议使用NOT EXISTS代替NOT IN
- IS NULL/IS NOT NULL:早期MySQL版本对NULL处理不佳可能失效,新版本已优化,但扔建议字段尽量设置NOT NULL
- USE INDEX等强制索引语句被优化器忽略:如果强制走索引但优化器判断效率更低,会忽略强制指令
三、如何避免索引失效
总结:
- 索引字段不做函数/运算,避免隐式类型转化
- 遵循组合索引的“最左前缀原则”
- LIKE查询尽量用前缀匹配(xxxx%)
- 用EXPLAIN分析SQL,关注type(是否为ALL)和Extra(是否有Using where)
- 结合业务场景设计索引
到此这篇关于MySQL索引失效的几种常见场景的文章就介绍到这了,更多相关MySQL索引失效内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!