联合索引未使用最左列则失效,因b+树按字段顺序构建;在索引列上使用函数或计算也会导致全表扫描,因索引存储原始值而非运算结果。

联合索引没用上最左列,索引直接“隐身”
MySQL 的联合索引(如 idx_user_status_time)本质是按字段顺序构建的 B+ 树,不是三个独立索引拼起来的。只要 WHERE 条件里跳过最左边的列(比如没写 user_id = ?),后续列再匹配也没用——优化器压根不会尝试走这个索引。
- ✅ 能用:
WHERE user_id = 1、WHERE user_id = 1 AND status = 0、WHERE user_id = 1 AND status = 0 AND create_time > '2025-01-01' - ❌ 失效:
WHERE status = 0、WHERE status = 0 AND create_time > '2025-01-01'(user_id缺失) - ⚠️ 容易踩坑:开发时只盯着业务字段写条件,忘了索引定义顺序;或把
status当主过滤条件,却没给它单独建索引
在索引列上套函数或计算,等于主动废掉索引
索引存的是原始值,不是函数结果。一旦写 DATE(create_time) 或 LEFT(name, 3),MySQL 就没法拿索引树里的值去比对——它得先把每行的 create_time 全算一遍 DATE(),再逐个比较,只能全表扫。
- ❌ 失效写法:
WHERE DATE(create_time) = '2025-01-01'、WHERE name + 'x' = 'Tomx'、WHERE YEAR(create_time) = 2025 - ✅ 改写建议:
WHERE create_time >= '2025-01-01' AND create_time ;查前缀用 <code>WHERE name LIKE 'Tom%'而非LEFT(name, 3) - ⚠️ 容易踩坑:ORM 自动生成 SQL 时悄悄加了
DATE();或者为图省事,在 WHERE 里直接格式化时间,没意识到代价
隐式类型转换让索引“认不出自己人”
字段是 INT,但查询传了字符串 '123',MySQL 会自动调用类型转换函数(相当于写了 CAST('123' AS SIGNED))。这和上面函数失效逻辑一样:索引树里存的是数字 123,而条件被转成函数表达式后,无法命中原始值。
- ❌ 失效场景:
WHERE id = '123'(id是INT)、WHERE user_code = 1001(user_code是VARCHAR) - ✅ 解决办法:参数类型严格对齐字段类型;用
EXPLAIN看type是否为ALL,再检查Extra里有没有Using where; Using index - ⚠️ 容易踩坑:Java/Python 传参时没注意类型(比如 MyBatis 的
#{}自动 toString);前端传 ID 字符串,后端没显式转 int 就拼进 SQL
InnoDB 和 MyISAM 对索引的“容忍度”其实没你想的那么不同
很多人以为 MyISAM 不支持事务所以索引“更弱”,其实两类引擎底层都用 B+ 树,索引失效的核心规则(最左前缀、函数、类型转换等)完全一致。真正差异在于:InnoDB 主键即聚簇索引,二级索引叶子存主键值,回表成本高;MyISAM 二级索引叶子存物理地址,回表快但不支持行锁。
- ❌ 误区:
LIKE '%abc'在 MyISAM 里就能走索引?不能。首部模糊对任何 B+ 树都无效 - ✅ 关键区别:InnoDB 中
SELECT *走二级索引大概率要回表,若数据量大、IO 高,优化器可能干脆放弃索引;MyISAM 同样语句也许走了索引但实际更慢(因无缓冲池、缓存效率低) - ⚠️ 容易忽略:线上库基本都是 InnoDB,别拿老资料里 MyISAM 的测试结论套用;判断索引是否生效,唯一靠谱方式是
EXPLAIN+ 真实数据量验证











