一、直接答案
对于联合索引 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):
- AB查询:✅ 完全走索引,利用A和B两列
- AC查询:⚠️ 部分走索引,仅利用A列,C通过索引下推过滤
- BC查询:❌ 通常不走索引(除非索引覆盖或跳跃扫描)
核心原因:联合索引在B+树中按A→B→C的顺序排列,后续列只在前面列相同时有序。
实战建议:
- 使用
EXPLAIN查看key_len判断实际使用了几列 - 高频AC查询可考虑添加
INDEX(A, C) - BC查询需要单独的
INDEX(B, C) - 平衡索引数量与写入性能
这道题考查对最左前缀原则的深入理解和实际应用能力。