索引失效是因为B+树仅存储原始值,无法匹配运算后的结果;如age + 1 = 25应改写为age = 24,确保索引列以裸值参与比较,否则优化器无法利用有序性定位。

因为索引树里存的是原始值,不是运算后的结果——MySQL没法拿age + 1去跟B+树节点里存的原始age值比对。
WHERE里写age + 1 = 25为什么不用索引
索引是按字段原始值构建的有序结构。当你在条件中对索引列做加减乘除、函数调用(如UPPER(name))、类型转换(如CAST(phone AS SIGNED)),MySQL就无法直接复用索引的有序性做快速定位。
-
age + 1 = 25等价于age = 24,但优化器不会自动反向推导,它只认原始列参与的等值或范围表达式 - 哪怕你建了
INDEX idx_age (age),只要WHERE里出现age * 2、age - 5、ABS(age),索引就失效 - EXPLAIN会显示
type=ALL或key=NULL,rows接近总行数
哪些数学运算一定让索引失效
所有对索引列本体的直接运算都会破坏索引匹配能力,包括:
- 算术运算:
WHERE price * 1.1 > 100、WHERE id + 100 = 200 - 内置函数:
WHERE FLOOR(score) = 85、WHERE ROUND(amount, 2) = 99.99 - 隐式转换:
WHERE user_id = '123'(而user_id是BIGINT)→ MySQL内部转成CAST(user_id AS CHAR),破坏索引
怎么改写才能继续走索引
核心原则:把运算从索引列挪到常量侧,保持索引列“裸露”出现在WHERE条件左侧。
- 错写:
WHERE create_time + INTERVAL 1 DAY = '2026-07-01'
正写:WHERE create_time = '2026-06-30' - 错写:
WHERE score / 10 >= 8
正写:WHERE score >= 80 - 错写:
WHERE ABS(status) = 1
正写:WHERE status IN (1, -1)(前提是status上有索引且基数合理)
联合索引里某列被运算,影响范围有多大
影响是“截断式”的:一旦最左前缀中的某一列因运算失效,它右边所有列的索引能力都归零。
- 索引
idx_user_status_created (user_id, status, created_at) -
WHERE user_id = 100 AND status + 1 = 2→ 只能用上user_id,status和created_at完全失效 -
WHERE user_id = 100 AND status = 1 AND created_at > NOW() - INTERVAL 7 DAY→ 三层全用上 -
key_len值会暴露这点:如果预估key_len是10,实际只有5,大概率中间某列被运算或类型不匹配卡住了
真正容易被忽略的是:即使运算逻辑简单、人眼可逆,MySQL优化器也不会帮你代数化简。它只认原始列是否以“干净”形式参与比较——这是B+树索引机制决定的硬约束,不是配置或版本能绕过的。


















