mysql b+树索引因函数操作无法匹配原始值而失效;date()、lower()、cast()等作用于索引列左侧的函数及隐式转换均导致索引失效;应改用范围查询、常量侧计算或前缀like替代。

函数操作让索引“看不见”原始值
MySQL 的 B+ 树索引只存原始列值的有序排列,不存函数计算后的结果。当你写 WHERE DATE(create_time) = '2023-01-01',优化器没法拿这个日期去查索引——因为索引里存的是 '2023-01-01 14:23:55' 这样的完整时间戳,不是单独的日期部分。
哪些函数会直接导致索引失效
只要出现在 WHERE 条件左侧、作用于索引列的函数,基本都踩坑:
-
DATE()、YEAR()、MONTH()、HOUR()等时间提取函数 -
LOWER()、UPPER()、SUBSTR()、TRIM()等字符串处理函数 -
CAST()、CONVERT()显式类型转换(哪怕你没写,隐式转换也一样) - 任何算术运算:
id + 1、price * 0.9、score - 5
怎么改写才能让索引重新生效
核心原则:让索引列“裸奔”,把计算逻辑移到常量侧或用范围替代。
- 时间函数 → 改成范围查询:
WHERE create_time >= '2023-01-01' AND create_time - 字符串大小写 → 统一存储格式,或用函数索引(MySQL 8.0+):
CREATE INDEX idx_name_lower ON user (LOWER(name)) - 数值运算 → 反推常量:
WHERE price > 90替代WHERE price + 10 > 100 - 前缀匹配需求 → 用
LIKE 'abc%',别用SUBSTR(name, 1, 3) = 'abc'
容易被忽略的隐性函数调用
有些写法看着不像函数,实则等价于函数操作:
-
WHERE phone = 13800138000(phone是VARCHAR)→ 隐式CAST(phone AS UNSIGNED) -
WHERE status != 1→ 优化器常认为结果集太大,放弃索引走全表扫描 -
WHERE name LIKE '%张%'→ 左侧通配符破坏前缀匹配,B+ 树无法跳转定位
真正难排查的不是显式函数,而是这些“看起来没问题”的写法——它们在执行计划里悄悄变成 type: ALL,却没人去 EXPLAIN 看一眼。











