是的,绝大多数情况下会失效;对索引列使用非单调、不可下推的函数(如upper、date、substring)会导致全表扫描,仅like前缀匹配、in、between、is null等操作能有效利用索引。

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(好) vsUsing 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) = ? 依然不能——函数索引只对定义时的表达式生效,不泛化。









