一、直接答案

对于联合索引 INDEX(A, B, C),各查询模式的索引使用情况:

查询条件 是否走索引 索引利用程度 说明
WHERE A=x AND B=y ✅ 完全使用 A、B两列 符合最左前缀
WHERE A=x AND C=z ⚠️ 部分使用 仅A列 跳过B,C无法使用
WHERE B=y AND C=z ❌ 不走索引 跳过最左列A

二、详细分析

1. WHERE A=x AND B=y

结论:✅ 完全走索引

EXPLAIN SELECT * FROM table WHERE A=1 AND B=2;

执行计划

type: ref
key: idx_abc
key_len: 8  -- 假设A、B各占4字节
Extra: Using index condition

原理

  • A是最左列,可以定位到A=1的范围
  • 在A=1的范围内,B列有序,可以进一步定位B=2
  • 充分利用了联合索引的有序性

B+树查找过程

索引树:
(1,1,x) → (1,2,x) → (1,3,x)
          ↑ 定位到这里
(2,1,x) → (2,2,x) → ...

2. WHERE A=x AND C=z

结论:⚠️ 部分走索引(仅A列)

EXPLAIN SELECT * FROM table WHERE A=1 AND C=3;

执行计划

type: ref
key: idx_abc
key_len: 4  -- 仅使用A列(4字节)
Extra: Using index condition

原理

  • A列可以使用索引定位到A=1的范围
  • 跳过了B列,在A=1范围内C列是无序的
  • C=3的过滤只能通过以下方式:
    • 索引下推:在索引层面过滤C=3(Extra: Using index condition)
    • 回表后过滤:在存储引擎层面过滤

B+树状态

A=1的范围内:
(1,1,1) → (1,1,3) → (1,2,1) → (1,2,3) → (1,3,1) → (1,3,3)
     ↑ C无序            ↑              ↑
只能定位A=1范围,然后在该范围内逐行检查C=3

性能影响

  • 如果A=1的数据量大,需要扫描很多行
  • 比完整走索引慢,但比全表扫描快

3. WHERE B=y AND C=z

结论:❌ 不走索引(通常)

EXPLAIN SELECT * FROM table WHERE B=2 AND C=3;

执行计划(通常)

type: ALL  -- 全表扫描
key: NULL
Extra: Using where

原理

  • 跳过了最左列A,B在全局范围内无序
  • 无法利用B+树的有序性进行定位
  • 优化器判断索引无用,直接全表扫描

特殊情况

-- 如果查询列都在索引中(索引覆盖)
SELECT A, B, C FROM table WHERE B=2 AND C=3;

-- 执行计划:
type: index  -- 索引全扫描
key: idx_abc
Extra: Using where; Using index

虽然不能定位,但全扫描索引比全扫描表快(索引更小)。

三、实战优化建议

场景1:频繁使用A和C查询

问题:WHERE A=x AND C=z 只能部分使用索引

解决方案

方案A:添加专用索引

-- 保留原索引
INDEX(A, B, C)

-- 新增索引
INDEX(A, C)

权衡

  • ✅ 查询性能提升
  • ❌ 额外存储空间
  • ❌ 写入性能下降(需维护多个索引)

方案B:调整索引顺序

-- 如果很少单独查询B,可以改为:
INDEX(A, C, B)

适用条件

  • 确认B单独查询频率很低
  • WHERE A=x AND C=z 的查询很频繁

场景2:需要BC查询

问题:WHERE B=y AND C=z 完全不走索引

解决方案

-- 创建新索引
INDEX(B, C)
-- 或如果也需要单独查C
INDEX(B, C) + INDEX(C)

场景3:索引覆盖优化

-- 如果查询可以改写为只查索引列
SELECT A, B, C FROM table WHERE A=1 AND C=3;

好处

  • 无需回表,直接从索引获取数据
  • Extra: Using index(覆盖索引)
  • 性能大幅提升

四、典型错误理解

错误1:认为AC可以完全走索引

错误认知:WHERE A=x AND C=z 能完全使用索引 ✅ 正确理解:只有A走索引,C需要索引下推或回表过滤

错误2:认为BC完全无法优化

错误认知:WHERE B=y AND C=z 完全不可能用索引 ✅ 正确理解

  • 索引覆盖时可以索引全扫描(优于全表扫描)
  • MySQL 8.0+ 在特定条件下可能使用索引跳跃扫描

错误3:盲目添加索引

错误做法:为每种查询组合都创建索引

INDEX(A, B, C)
INDEX(A, C)
INDEX(B, C)
INDEX(A, B)
-- ... 过多冗余索引

正确做法

  • 分析查询频率和数据分布
  • 平衡查询性能和写入性能
  • 使用慢查询日志确定关键查询

五、EXPLAIN验证

-- 准备测试
CREATE TABLE test (
    A INT,
    B INT,
    C INT,
    data VARCHAR(100),
    INDEX idx_abc(A, B, C)
);

-- 测试1:AB查询
EXPLAIN SELECT * FROM test WHERE A=1 AND B=2;
-- key_len: 8 (两列都用上)

-- 测试2:AC查询
EXPLAIN SELECT * FROM test WHERE A=1 AND C=3;
-- key_len: 4 (只有A列)

-- 测试3:BC查询
EXPLAIN SELECT * FROM test WHERE B=2 AND C=3;
-- type: ALL 或 index (取决于数据量)

关键指标

  • key_len:实际使用的索引长度(判断用了几列)
  • type:访问类型(ref > range > index > ALL)
  • Extra:额外信息(Using index = 覆盖索引)

六、面试总结

对于联合索引 INDEX(A, B, C)

  1. AB查询:✅ 完全走索引,利用A和B两列
  2. AC查询:⚠️ 部分走索引,仅利用A列,C通过索引下推过滤
  3. BC查询:❌ 通常不走索引(除非索引覆盖或跳跃扫描)

核心原因:联合索引在B+树中按A→B→C的顺序排列,后续列只在前面列相同时有序。

实战建议

  • 使用 EXPLAIN 查看 key_len 判断实际使用了几列
  • 高频AC查询可考虑添加 INDEX(A, C)
  • BC查询需要单独的 INDEX(B, C)
  • 平衡索引数量与写入性能

这道题考查对最左前缀原则的深入理解和实际应用能力。