mysql索引失效的原因可以分为五个方面来讨论:
--违反最左前缀法则
--范围查询导致后续列失效
--在索引列上进行函数运算操作
--字符串不加单引号的情况
--以%开头的like模糊查询
一、首先,违反最左前缀法则:
当然前提是在查询时使用了联合索引的情况下
关于为什么违反最左前缀法则,我们应该回到mysql中innodb引擎所使用的索引存储结构来刨析
innodb 引擎使用 b+树 作为索引结构,而违反最左前缀法则导致索引失效的核心本质就是:b+树索引的有序性依赖
若在age,city,name三列上创建联合索引,其在b+树的叶子结点中的存储就会是:
(假设有这些数据)

而根节点存储的就是最左边age的范围,以通过age进行索引遍历到对应的叶子结点
b+树索引遍历的关键特性:
- 全局有序:整个索引按 age 第一优先级排序
- 局部有序:在 age 相同的情况下,按 city 排序
- 再局部有序:在 age 和 city 都相同的情况下,按 name 排序
若where 指定多个条件时,最左面的条件是age=20,就会根据根节点中的age进行遍历,
找到age,再根据叶子结点上的city进行查找,最后根据name进行查找

若where最左面的条件指定的是city=“北京”,由于根节点只存储最左边age的范围,而city的值只存储在最后的叶子结点上,无法根据city快速定位对应的叶子结点上,此时只能进行全表扫描挨个行匹配,造成索引失效
二、范围查询导致后续列失效
同样是b+树存储结构原因
参照之前提到过的b+树索引遍历的关键特性:
- 全局有序:整个索引按 age 第一优先级排序
- 局部有序:在 age 相同的情况下,按 city 排序
- 再局部有序:在 age 和 city 都相同的情况下,按 name 排序
若where age > 20 and city = '北京'
使用 age > 20 定位到叶子结点,这可以走索引
问题在于 age > 20 的范围内, city 是无序的!
如图:

在age都等于21时,其city再按照字符的ascii码进行排序,“北京”<“广州”,排序好了两条
在age都等于22时,也完成了排序,排序出了一条
此时这两项拼接加一起时却是如上图所示的结构。
city不是按照字符顺序连续存储,city没有顺序就不能对city进行连续的扫描,只能在 age > 20 的结果集中,逐行过滤 city='北京' ,city列的索引失效
三、在索引列上进行函数运算操作
假设我们在 create_time 列上创建了索引,其该列的值有:

若进行where year(create_time) = 2023 时,是匹配不到的
索引存储的是"原始值"而非"计算结果",只存储原始时间字符串(如’2023-01-15 10:30:00‘),不存储 year(create_time) 的计算结果2023,因此函数运算破坏了"导航能力",导致索引失效,只能逐行读取每条数据,计算 year(create_time) ,再比较。
四、字符串不加单引号的情况
这是开发中最常见、最隐蔽的索引失效陷阱
=右面没有加单引号
select * from users where phone = 13800138000;
底层机制:mysql 的隐式类型转换规则
当字符串列与数字比较时,mysql 总是将字符串转换为数字,而不是反过来,原版phone是字符类型,但却被转换为数值类型。
而索引中存储的是字符类型的phone,无法与数值类型的phone匹配,导致索引失效
五、以%开头的like模糊查询
假设 name 列的索引 idx_name 存储以下数据:
- name: '张三', '张三丰', '李四', '李小四', '王五'
比较规则是从第一个字符进行比较,之后是第二个字符比较以此类推
- 若where name like ’张三%‘
就会从根节点匹配第一个字符‘张’,找到对应的子结点。
- 若where name like ’%三‘
此时字符串以前缀模糊匹配,根本不知道第一个字符是什么,无法从根结点快速定位到叶子结点上,只能全表扫描,索引失效
因此这就是为什么使用like进行模糊查询时,只有前缀模糊索引会失效。
以上就是造成索引失效的具体原因,相信经过上述的分析,之后在索引的使用中可以做到避免索引失效的情况,也理解该如何使用索引,并对其进行优化。
到此这篇关于mysql中索引失效原理深入解析的文章就介绍到这了,更多相关mysql索引失效原理内容请搜索代码网以前的文章或继续浏览下面的相关文章希望大家以后多多支持代码网!
发表评论