MySQL 联合索引最左前缀失效的六个真实场景
建了联合索引 (a, b, c),查询却走了全表扫描。这不是索引没生效,而是写法和索引结构对不上。下面这六种情况在实际项目里最常见。
一、跳过了最左列。where b = 1 and c = 2 用不上 (a, b, c),因为 B+ 树先按 a 排序,a 不定下来后面无法定位。想让 b、c 生效,得单独给 (b, c) 建索引。
二、范围查询截断后续列。where a = 1 and b > 10 and c = 3,b 用了范围之后 c 无法再走索引定位。这时候可以考虑把 c 提到 b 前面,或者用覆盖索引绕过。
三、对索引列做函数运算。where date(create_time) = '2026-01-01' 会让索引失效,改成 create_time >= '2026-01-01' and create_time < '2026-01-02',范围查询仍然走索引。
四、隐式类型转换。字段是 varchar,查询写 where phone = 13800138000(数字),MySQL 会把字段转成数字比较,索引直接失效。加上引号就好。这一条在代码里最容易漏。
五、like 以通配符开头。like '%abc' 无法使用索引,like 'abc%' 可以。真要做前后模糊匹配,得上全文索引或者搜索引擎。
六、or 连接非索引列。where a = 1 or d = 2,只要 d 没索引,整个条件就会退化成全表扫描。把 or 拆成两条 union,或者给 d 也建索引。
排查手段:explain 看 type 和 key 两列,type 出现 ALL 就是全表扫描,key 为 null 说明没用上索引。再看 possible_keys 里有但 key 是 null,基本就是上面某一种。explain analyze 在 8.0 上还能看到实际行数,比估算更准。
另一个容易忽略的点:索引列的选择性。如果 a 只有两个取值,放在最左边几乎没过滤效果,把区分度高的列往前放,或者建联合索引时考虑 cardinality。用 show index from 表名 看 Cardinality 字段能判断。
- 点赞
- 收藏
- 关注作者
评论(0)