MySQL 索引列用在函数里,索引真的会失效
直接说结论:是的,绝大多数情况下会失效。只要你在
WHERE条件中对索引列施加了**非单调、不可下推的函数调用**,优化器就无法利用该索引做范围扫描或等值查找。
典型失效场景包括:
WHERE UPPER(name) = 'JOHN'、
WHERE DATE(create_time) = '2024-01-01'、
WHERE SUBSTRING(phone, 1, 3) = '138'。这些写法会让 MySQL 被迫对每一行先计算函数结果再比对,彻底绕过索引。
例外情况极少:比如
WHERE col + 0 = 123(隐式类型转换)或
WHERE col * 1 = 123,某些版本可能仍能走索引,但完全不可依赖,也不建议这么写。
哪些函数操作可能“保索引”?要看是否支持索引下推和单调性
MySQL 5.7+ 支持部分函数的索引下推(ICP),但前提是函数本身可下推且不改变比较语义。目前真正安全可用的只有:
WHERE col LIKE 'abc%'—— 前缀匹配,B+ 树天然支持
WHERE col IN ('a', 'b', 'c') —— 多个等值,仍可走索引查找
WHERE col BETWEEN 10 AND 20—— 范围查询,索引有效
WHERE col IS NULL或
col IS NOT NULL—— 对可空索引列也有效(需注意联合索引最左前缀规则)
像
YEAR(create_time) = 2024这种看似简单,实际会触发全表扫描;而改写成
create_time >= '2024-01-01' AND create_time 才能命中索引。
EXPLAIN 看不到 key?别急着改 SQL,先确认执行计划细节
EXPLAIN中
key为
NULL是索引失效的强信号,但不是唯一依据。更关键要看:
type是否为
ALL或
index(全表/全索引扫描)
rows是否远大于实际匹配数(说明没剪枝)
Extra是否出现
Using where; Using index(好) vs
Using where(坏,回表+无索引过滤)
有时索引“看似没被选中”,其实是优化器基于统计信息判断走全表更快(比如表小、条件选择率高)。这时
FORCE INDEX可验证,但不推荐线上滥用。
替代方案:函数索引(MySQL 8.0.13+)和生成列是正解
如果业务真绕不开函数逻辑,优先考虑:
创建函数索引:CREATE INDEX idx_name_upper ON users ((UPPER(name)))—— 注意双括号语法,只在 MySQL 8.0.13+ 支持 用生成列 + 普通索引:
ALTER TABLE users ADD name_upper VARCHAR(64) GENERATED ALWAYS AS (UPPER(name)) STORED;</code><br><code>CREATE INDEX idx_name_upper ON users (name_upper);应用层预计算并存入普通字段(如
name_lower),索引、查询、更新都更可控
函数索引对
JSON字段提取、时间截断等场景特别有用,但要注意它不支持全文索引、空间索引,且维护成本略高。
最常被忽略的一点:即使用了函数索引,
WHERE UPPER(name) = ?能走,但
WHERE LOWER(name) = ?依然不能——函数索引只对定义时的表达式生效,不泛化。
