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' ❌ 无法定位起始位置
口诀:
等值查询 → 缩小范围 → 后面的列继续有效
范围查询 → 扩张到多个区间 → 后面的列只在各自区间内有序 → 无法用于缩小范围
浙公网安备 33010602011771号