MySQL符合索引与最左前缀原则

对于联合索引 INDEX(a, b, c)

索引排序顺序:先按 a 排序,a 相同按 b 排序,b 相同按 c 排序

能用到索引的情况:
  WHERE a = 1                      ✅ 用到 a
  WHERE a = 1 AND b = 2            ✅ 用到 a, b
  WHERE a = 1 AND b = 2 AND c = 3  ✅ 用到全部
  WHERE a = 1 AND c = 3            ✅ 用到 a(c 不能跳过 b 单独用)
  WHERE a > 1 AND b = 2            ✅ a 范围后 b 失效(部分场景可用 ICP)

用不到索引的情况:
  WHERE b = 2                      ❌ 跳过 a
  WHERE c = 3                      ❌ 跳过 a, b
  WHERE b = 2 AND c = 3            ❌ 跳过 a

索引列的顺序 = 排序的优先级。跳过前面的列,后面的列无法用于索引查找。

因为复合索引用一个 B+Tree 实现了多级排序。想象一张排好序的纸质表格就懂了:


复合索引 (city, status) 的物理结构

索引 B+Tree 叶子节点按顺序排列:

city='Beijing'  status=0
city='Beijing'  status=0
city='Beijing'  status=1    ← Beijing 里面按 status 排序
city='Beijing'  status=1
city='Beijing'  status=1
────────────────────────────    ← 换下一个城市
city='Shanghai' status=0
city='Shanghai' status=1
city='Shanghai' status=1
────────────────────────────
city='Shenzhen' status=0
city='Shenzhen' status=0
city='Shenzhen' status=1
...100个城市,每个城市里面 status 有序...

问题一:跳过 city 直接查 status=1 为什么失效?

你问 B+Tree:把所有 status=1 的找出来

B+Tree 傻眼了:

city='Beijing' status=0 ← status=1 在哪?
city='Beijing' status=0 ← 这里?
city='Beijing' status=1 ← 这里?跳到后面再跳回来?
city='Beijing' status=1
──────────────────────
city='Shanghai' status=0
city='Shanghai' status=1 ← 这里有
city='Shanghai' status=1
──────────────────────
city='Shenzhen' status=0
city='Shenzhen' status=0
city='Shenzhen' status=1 ← 这里有
...

status=1 的行散落在 100 个城市各自的区间里,没有连续聚集在一起
→ 无法定位一个"连续区间" → 只能把整个索引扫一遍 → 失效

本质:不指定 city,B+Tree 里 status=1 的记录不连续,没法用 B+Tree 的"定位起点 → 顺序扫描"能力。


问题二:city 用了范围后,status 为什么也失效?

你问 B+Tree:city LIKE 'Bei%' AND status = 1

city 前缀匹配可能命中多个城市:

city='Bei...' status=? ← 可能有 Beijing, Beihai, Beilun...
city='Beijing' status=0
city='Beijing' status=0
city='Beijing' status=1 ← status=1
city='Beijing' status=1 ← status=1
─────────────────────────
city='Beihai' status=0
city='Beihai' status=0
city='Beihai' status=1 ← status=1
city='Beihai' status=1 ← status=1
─────────────────────────
city='Beilun' status=1 ← status=1
city='Beilun' status=1 ← status=1
...

status=1 在这个大范围内,按 city 排序,而不是按 status 排序
→ 无法连续定位所有 status=1 → 只能扫完整个 city 范围再逐个判断

本质:city 不精确(等值=一个区间),status 只在每个 city 区间内部有序,跨了 city 区间就不连续了。


一张图总结

最左前缀命中的条件:
  city = ?  AND  status = ?     ✅ 定位到连续区域
  city = ?  AND  status > ?     ✅ 同一个 city 内 status 连续
  city = ?                      ✅ 定位到 city 区间
  city > ?                      ✅ 定位到起始 city,顺序往后扫

失效的情况:
  status = ?                    ❌ 散落在 100 个 city 区间里
  city > ?  AND  status = ?     ❌ 多个 city 区间,status=1 不连续
  city LIKE '%x'                ❌ 无法定位起始位置

口诀:
等值查询 → 缩小范围 → 后面的列继续有效
范围查询 → 扩张到多个区间 → 后面的列只在各自区间内有序 → 无法用于缩小范围

posted on 2026-08-12 16:35  幽州散人  阅读(12)  评论(0)    收藏  举报

导航