索引失效类型和原因--1.对索引列使用函数
(1)对索引列使用函数
❌ 索引失效
SELECT * FROM my_table WHERE LEFT(column1, 3) = 'abc';
✅ 索引有效
SELECT * FROM my_table WHERE column1 LIKE 'abc%';
2. 索引失效的详细原因
(1)LIKE '%abc'
失效
- MySQL B+Tree 索引按照前缀匹配,必须从最左侧开始匹配。
'abc%'
可以在索引中找到起点,然后顺序查找,索引有效。'%abc'
需要扫描整个表,逐个计算匹配,索引失效。
(2)LEFT(column1, 3) = 'abc'
失效
- 索引存的是完整列值,但
LEFT(column1, 3)
计算的是新值。 - MySQL 无法在索引中找到计算后的值,只能遍历整个表计算,导致索引失效。
为什么 LEFT(column1, 3) = 'abc'
会导致索引失效
1. MySQL 索引的工作原理
MySQL 的 B+Tree 索引按照列的原始值存储数据,索引的作用是帮助数据库快速找到目标数据,类似于字典的目录。
比如,我们有一个索引如下:
column1 原始值 | 索引存储的内容 |
---|---|
“abcdef” | “abcdef” |
“abcxyz” | “abcxyz” |
“xyzabc” | “xyzabc” |
“123abc” | “123abc” |
索引按照完整的字符串排序,所以如果你查询:
SELECT * FROM my_table WHERE column1 = 'abcdef';
MySQL 可以直接通过索引找到 'abcdef'
这个值,而不需要扫描整个表。
2. LEFT(column1, 3) = 'abc'
是如何执行的?
SELECT * FROM my_table WHERE LEFT(column1, 3) = 'abc';
这时 MySQL 需要先计算 LEFT(column1, 3)
,然后再比较。 但索引里存储的是完整的 column1
值,而索引不能直接存储 LEFT(column1, 3)
计算的结果。
为什么索引无效?
MySQL 需要先对每一行的数据执行 LEFT(column1, 3)
计算,看看前三个字符是不是 'abc'
。 但索引无法存储计算后的值,所以 MySQL 不能直接用索引找到结果,而是必须遍历所有数据(全表扫描)。索引失效的核心原因之一是 MySQL 找不到起始位置!
为什么 LEFT(column1, 3) = 'abc'
找不到起始位置?
1. MySQL 索引是按 完整的 column1
值 排序的
B+Tree 索引本质上是一个有序结构,它存储的顺序如下(假设 column1
建立了索引):
column1 值 | 存储在索引中的数据 |
---|---|
“abc123” | ✅ 有索引 |
“abc456” | ✅ 有索引 |
“abcd789” | ✅ 有索引 |
“xyz000” | ✅ 有索引 |
索引的作用是:能否直接找到起点并高效扫描。 但 LEFT(column1, 3) = 'abc'
破坏了索引的顺序查找能力。
👉 打个比方:
LIKE 'abc%'
就像是直接查字典,找到 “abc” 开头的单词,然后顺序翻页,很快就能查到。LEFT(column1, 3) = 'abc'
相当于必须检查字典里的每个单词,把它前 3 个字母取出来对比,再决定是否符合,必须翻完整本字典,速度极慢。
3. 解决方案
. LIKE 'abc%'
可以找到索引起点
✅ 方法 :用 LIKE
替代 LEFT()
SELECT * FROM my_table WHERE column1 LIKE 'abc%';
- MySQL 可以找到索引的起点(例如 “abc123”)。
- 然后按索引顺序扫描匹配的行,直到遇到第一个不满足
LIKE 'abc%'
的值(例如 “xyz000”)。 - 这样查询就非常快,索引是有效的。
✅ 这个查询能用索引,是因为它可以在 B+Tree 里找到第一个 abc
开头的值,然后顺序扫描。
为什么这样能用索引?
'abc%'
直接匹配 前缀,MySQL 可以用索引 快速定位'abc'
开头的数据,而不需要计算每一行的值。
总结
- MySQL 索引存的是完整的原始值,不存储计算后的值。
- 对索引列使用函数(如
LEFT(column1, 3)
)时,MySQL 不能利用索引,必须对每一行计算,导致全表扫描。 - 避免在
WHERE
里对索引列使用函数,改用LIKE 'abc%'
或者创建一个额外的存储列加索引,提高查询效率。