mysql联合索引必须从最左列开始连续匹配,本质是b+树按a主序、b次序、c末序构建的单棵树结构决定的:a全局有序可二分查找,b仅在a固定时局部有序,c仅在a和b都固定时局部有序;跳过a则无法定位起点,故where b=2等查询无法使用索引。

MySQL联合索引必须从最左列开始连续匹配,不是MySQL“规定”,而是B+树物理结构决定的——跳过左侧字段查右侧,就像翻电话簿不看姓氏直接找名字,只能全表扫描。
为什么WHERE b = ?无法使用(a, b, c)联合索引
B+树叶子节点按a全局排序;a相同时再按b排序;a和b都相同时才按c排序。这意味着b列在整棵树里是分散无序的。执行WHERE b = 2时,MySQL无法定位到某一段连续页,只能逐页扫描,索引失效。
此时EXPLAIN中key为NULL,type通常是ALL或index,Extra里带Using where。
-
WHERE a = 1 AND b = 2→key_len包含a和b长度,走索引查找 -
WHERE b = 2 AND c = 3→key为NULL,退化为全表扫描 -
WHERE a = 1 AND c = 3→key_len只反映a,c靠Server层逐行过滤
ORDER BY和GROUP BY也受最左前缀约束
索引的有序性仅在“前缀连续满足条件”的子树内成立。比如(status, create_time, user_id)索引:
-
ORDER BY status, create_time✅ 可免排序(Using filesort不出现) -
ORDER BY create_time, user_id❌ 必触发Using filesort -
WHERE status > 'done' ORDER BY create_time❌ 即使create_time在索引第二位,因status是范围查询,对应子树中create_time无序
注意:WHERE条件顺序不影响优化器重排,但ORDER BY字段顺序必须严格对齐索引定义顺序。
设计联合索引时如何排字段顺序
顺序不是拍脑袋定的,核心是让高频、高区分度、等值过滤的列尽可能靠左,把范围查询和排序字段往右放。
- 等值条件多且稳定(如
user_id = ?、tenant_id = ?)→ 放最左 - 多个等值条件时,优先放基数高(值更分散)的列,减少扫描行数
- 范围查询列(
create_time > ?、amount BETWEEN ? AND ?)→ 尽量靠右,它之后的列无法用于查找 -
ORDER BY字段若固定,可放末尾,但前提是前面所有列都能被WHERE充分过滤 - 已有
(a, b, c),通常无需再建(a, b)——后者被前者完全覆盖
容易被忽略的细节:覆盖索引与key_len含义
即使某些列没参与查找,只要它们在索引中且SELECT只查这些字段,仍可能走“索引覆盖”,避免回表。但key_len只反映实际用于查找的列长度,不等于整个索引长度。
例如索引(a, b, c),执行WHERE a = 1 AND b > 10,key_len只含a和b,c虽在索引里但不参与定位;若SELECT a, b, c,仍可能Using index,但c不是靠索引结构查出来的,而是直接从叶子节点读出。
真正难的是判断哪些查询“看起来能走索引”,实则只有左半截生效——得看EXPLAIN里的key_len和Extra,不能只信possible_keys有值。











