mysql中对索引列使用函数(如upper、date)会导致索引失效,因b+树基于原始值排序;应改用范围查询或mysql 8.0+函数索引,并通过explain的key和type字段确认。

MySQL 函数会破坏索引的使用
只要在 WHERE 子句中对索引列施加函数(如 UPPER()、DATE()、SUBSTRING()、YEAR()),MySQL 通常无法走该列上的索引,即使索引存在且匹配查询条件。本质原因是:索引是按原始列值有序存储的,而函数改变了值的形态或范围,优化器无法直接定位 B+ 树中的位置。
-
SELECT * FROM users WHERE UPPER(name) = 'JOHN'→ 即使name有索引,也会全表扫描 -
SELECT * FROM orders WHERE DATE(created_at) = '2024-01-01'→created_at索引失效,应改用范围查询 - 例外:MySQL 8.0+ 对部分函数(如
JSON_EXTRACT()配合生成列)支持函数索引,但需显式创建
如何判断函数是否导致索引失效
核心方法是看 EXPLAIN 输出中的 key 和 type 字段。若 key 为 NULL 或 type 是 ALL,基本可确认索引未被使用。
EXPLAIN SELECT * FROM logs WHERE YEAR(log_time) = 2024;
对比优化后的写法:
EXPLAIN SELECT * FROM logs WHERE log_time >= '2024-01-01' AND log_time < '2025-01-01';
- 前者
key: NULL,后者key显示实际使用的索引名 - 注意:即使函数出现在
SELECT列表(如SELECT UPPER(name)),只要WHERE里没动索引列,不影响索引选择
函数索引(MySQL 8.0+)是绕过限制的可行方案
当业务逻辑强依赖函数查询(如大小写不敏感匹配、日期截断),又不能改应用层时,可建函数索引。它把函数计算结果持久化为虚拟列并索引,代价是额外存储和维护开销。
- 必须用
PERSISTENT虚拟列 + 普通索引,或直接用 MySQL 8.0 的函数索引语法 - 示例:让
email字段忽略大小写查询
CREATE INDEX idx_email_lower ON users ((LOWER(email)));
- 之后
WHERE LOWER(email) = 'a@b.com'就能命中该索引 - 注意:函数索引只支持确定性函数(如
LOWER、TRIM),不支持NOW()、RAND()等
常见“看似没函数,实则隐含函数调用”的场景
有些写法表面没显式函数,但 MySQL 内部做了类型转换或隐式处理,同样导致索引失效。
- 字符串字段存数字(如
status VARCHAR(10)),却写WHERE status = 1→ 触发隐式类型转换,索引失效 - 字符集/排序规则不一致:
utf8mb4_0900_as_cs列与utf8mb4_general_ci常量比较,可能放弃索引 - 使用
LIKE '%abc'开头通配符,本质等价于SUBSTRING(col, ...),B+ 树无法跳查
这类问题往往比明面上的函数更难察觉,排查时要连带检查 SHOW CREATE TABLE 和 EXPLAIN FORMAT=TRADITIONAL 的 Extra 提示(如出现 Using where; Using index 是好的,Using filesort 或 Using temporary 则需警惕)。











