题目:MySQL 中 A,B,C 的联合索引,按照 AB, AC, BC 查询,能走索引吗?
难度:medium
标签:db/mysql/index, mysql/index, difficulty/medium
评分:⭐⭐⭐☆(3/5)
首先联合索引 ABC,按顺序来的话,ABC 间的索引 AB 跟 AC 是可以走部分的联合索引的,但是 BC 基本上不会走到联合索引。当然也有情况可能会走,就是 A 的数据很少,并且 A 的数据很单一的情况下,比如 A 是一个状态,这时候它可能做一个索引下推,在数据内部会做优化的时候,会进行类似于 UNION 将每个数据 UNION 一遍的情况。但正常情况下,B 级的查询是不会走索引的。想要做 B 级查询,可以单独建 B 级前缀的索引。
优点:
不足:
EXPLAIN Extra 的关键标识(Using index for skip scan vs Using index condition)FORCE INDEX 陷阱:即使 force 也可能只是全索引扫描,比全表扫描还慢补充:
Extra 显示 Using whereUsing index for skip scan,把查询拆成多个 A=? 的子查询在索引内部完成核心考点定位:理解联合索引的排序规则(B+ 树复合键排序)与 MySQL 8.0 的 Skip Scan 优化边界。
知识点拆解:
(A,B,C) 复合键的 B+ 树按字典序排序——先按 A 排序,A 相同时再按 B 排序,B 相同时再按 C 排序。所以查询能否命中索引取决于"是否提供了前缀"。WHERE a=? AND b=? 完整命中 A、B 两段定位。若没有 C 条件,C 不能作为定位,只能作为过滤(回表后判断)。WHERE a=? AND c=? 只有 A 段能定位,C 段中间隔了 B,无法利用排序,C 走过滤,Extra=Using where。WHERE b=? AND c=? 默认走不到索引——因为 A 未知,B 的排序在不同 A 之间不连续。MySQL 8.0+ 例外:若 A 基数很低(如 gender=0/1),优化器可能做 Skip Scan,把查询拆成 A=0 AND B=? AND C=? 和 A=1 AND B=? AND C=? 两段在索引内部完成,Extra=Using index for skip scan。Extra=Using index condition。它跟 Skip Scan 是两回事——ICP 减少回表次数,Skip Scan 让原本走不到的索引能走到。key 是否是目标索引、rows 是否显著下降、Extra 是否出现 Using index(覆盖)、Using index condition(ICP)、Using index for skip scan(Skip Scan)。考察方向:
EXPLAIN 判断实际执行计划并给出改进方案延伸递进面试题:
WHERE a IN (1,2,3) AND b=? 会走联合索引吗?WHERE a>? AND b=? 会怎样?为什么范围查询后面的列用不上?FORCE INDEX 能强行让 BC 走 (A,B,C) 索引吗?会有什么问题?EXPLAIN 验证。面试官您好,这个问题关键要看联合索引的排序规则。(A,B,C) 复合索引在 B+ 树上按字典序排——先按 A 排,A 相同再按 B 排,B 相同再按 C 排。所以查询要能命中索引,就必须能提供前缀。
AB 查询 WHERE a=? AND b=?:A 定位到一片区间,这片区间内 B 是有序的,所以 B 也能定位,两段都能用;如果 WHERE 没有 C,C 只能作为过滤条件在回表后判断。
AC 查询 WHERE a=? AND c=?:A 定位到区间后,中间隔了一个 B 未知,C 在这个区间里不再有序,所以 C 用不上索引定位,只能走过滤。EXPLAIN 会看到 Extra=Using where,key_len 只覆盖 A 段。
BC 查询 WHERE b=? AND c=?:默认走不到 (A,B,C) 索引。因为 A 未知,B 的有序性被 A 的分隔打散。但这里有个例外,MySQL 8.0 有个 Skip Scan(跳跃扫描) 优化:如果 A 的基数很低,比如是性别、状态这种只有几个值的字段,优化器会把这个查询在索引内部拆成 A=0 AND B=? AND C=?、A=1 AND B=? AND C=? 等多段分别扫描再合并。EXPLAIN 的 Extra 会显示 Using index for skip scan。
这里容易混淆的一个概念是 ICP(索引下推)。ICP 是 MySQL 5.6 加的优化,把 WHERE 里非索引列的条件前推到索引扫描阶段做过滤,减少回表次数,Extra 显示 Using index condition。ICP 不会让走不到的索引变走得到,它只是减少回表。Skip Scan 才是让原本用不到前缀列的查询也能用上索引。
所以工程实践上,如果有明确的 BC 查询模式,还是单独建 (B,C) 索引更稳妥,不要指望 Skip Scan 兜底——它的触发条件比较苛刻,A 基数低是硬要求。另外也不要用 FORCE INDEX 硬推,因为可能退化成全索引扫描,比全表扫描还慢。
https://chat.deepseek.com/share/6snsbux2u1oe10v9gu