索引失效是因为b+树只存原始列值,函数计算或运算导致优化器无法映射查找路径,只能全表扫描;如amount*100>5000、date_format(create_time,'%y-%m')='2023-10'均因运行时计算破坏索引有序性。

因为索引(B+树)里存的是原始列值,不是计算后的结果;一旦在 WHERE 里对索引列做加减乘除、函数调用或类型转换,MySQL 就没法跳过无关数据直接定位,只能逐行计算再比对——这等于放弃索引,退化成全表扫描。
WHERE 中写 amount * 100 > 5000 为什么不用索引?
索引节点按 amount 原始值有序排列,但 amount * 100 的结果没有预存、也无法推导出有序范围。优化器无法把该表达式映射到 B+ 树的查找路径上,只能扫全表算每行的 amount * 100。
- 哪怕
amount上有索引,amount * 100 > 5000也等价于amount > 50,但 MySQL 不会自动帮你化简 - 改写为
amount > 50就能走索引——前提是运算可逆且无精度丢失(比如除法要小心浮点误差) - 如果业务真需要按“百元单位”查,更稳的方式是建冗余字段
amount_cents INT并索引它
DATE_FORMAT(create_time, '%Y-%m') 为何让日期索引失效?
DATE_FORMAT() 是运行时函数,每行都要执行一次,输出字符串后再比对。而 create_time 索引里存的是 DATETIME 类型的二进制值,两者不可对齐。
- 错误写法:
WHERE DATE_FORMAT(create_time, '%Y-%m') = '2023-10' - 正确写法:
WHERE create_time >= '2023-10-01' AND create_time - MySQL 8.0+ 可建函数索引:
CREATE INDEX idx_month ON 表名 ((DATE_FORMAT(create_time, '%Y-%m'))),但注意该索引只服务这个固定格式
隐式类型转换比显式运算更隐蔽地导致失效
当索引列是 VARCHAR,却用数字查询,比如 WHERE user_id = 123,MySQL 会把每行 user_id 字符串转成数字再比——这个转换发生在引擎层,不报错但彻底废掉索引。
- 典型现象:慢查询日志里
rows_examined接近表总行数,key字段为空 - 确认方式:
EXPLAIN SELECT ...看type是否为ALL,key是否为NULL - 修复只需加引号:
WHERE user_id = '123';或者统一字段类型,避免 VARCHAR 存编号
最麻烦的不是写错,而是“看起来能走索引”的表达式实际没走——比如 WHERE col + 0 = 123 或 WHERE col = ? + 0,只要左侧出现任何形式的计算,索引就断了。上线前务必用 EXPLAIN 验证,别信直觉。











