mysql b+树索引因表达式计算失效,因索引只存原始值与排序关系;算术运算、函数调用、类型转换及隐式转换均导致全表扫描;应将计算移至条件右侧或用范围替代函数以重用索引。

表达式计算破坏索引值的原始有序性
MySQL 的 B+ 树索引只存储列的原始值及其排序关系,不存储任何计算结果。当你写 WHERE age + 1 > 25,优化器无法把“age + 1”映射回索引中已排序的 age 值序列——它得对每一行的 age 先加 1、再比较,等价于全表扫描。
哪些表达式会触发失效
只要出现在 WHERE 或 ORDER BY 中且作用于索引列,以下形式都会让索引失效:
-
age * 1.2、age - 5等算术运算 -
UPPER(name)、DATE(create_time)等函数调用 -
CAST(age AS CHAR)、CONVERT(age, CHAR)等显式类型转换 - 隐式转换,比如
WHERE phone = 13800138000(phone是VARCHAR)
怎么改写才能重用索引
核心原则是:让索引列以“裸值”形式独立出现在条件左侧。
- 把计算移到右侧:
WHERE age > 25 - 1→WHERE age > 24 - 用范围替代函数:
WHERE YEAR(create_time) = 2023→WHERE create_time >= '2023-01-01' AND create_time - 冗余存储计算结果(MySQL 5.7+ 支持生成列):
ADD COLUMN age_plus_1 INT AS (age + 1) STORED,再给该列建索引
容易被忽略的边界情况
即使没写函数,某些看似安全的操作也会踩坑:
-
WHERE age + 0 = 25—— 仍是表达式,索引失效 -
WHERE (age) = 25—— 括号不改变语义,但部分旧版本优化器可能误判,建议去掉 - 联合索引中,仅对最右列做计算,比如
INDEX(a, b, c)中写WHERE a=1 AND b=2 AND c+1=10,前两列能用,c列仍失效











