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 的函数索引语法 示例:让
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则需警惕)。
