题目:a,b两个单独索引,where a=xx and b=xx 走哪个索引?为什么?
难度:medium
标签:db/mysql/index, mysql/index, difficulty/medium
评分:⭐⭐⭐⭐☆(4/5)
具体装哪个索引不一定,MySQL 会根据索引的具体区分度以及使用索引的成本来规划优化选择哪个索引去使用。
但是如果这么一个搜索条件的话,并且经常使用,推荐建 A 跟 B 的联合索引,让两个字段都能被使用到,提升效率。
这两个字段 A 跟 B,看优化器选择,哪个区分度高,筛选的成本更少,能取到更少,能在更少的数据集中找到目标结果。一般优化器会选择对应的索引,并不会根据直接查看就会知道使用哪个索引。但是如果使用联合索引,将两个数据字段都使用上,这样就可以保证更好的优化,更少的数据集被选择。
优点:
(a, b) 联合索引不足:
WHERE a=xx AND b=xx 有 index_merge 优化,可能走两个单列索引 UNION,EXPLAIN 显示 Using intersect(idx_a, idx_b) 或 Using union(...)——但这通常代价高,优化器一般不选EXPLAIN 看 key、type、Extra、rows、filtered 字段FORCE INDEX / USE INDEX / IGNORE INDEX(生产慎用)information_schema.statistics 的 Cardinality,统计信息不全会选错索引补充:
key:实际选择的索引rows:预估扫描行数filtered:预估过滤后剩下的百分比Extra:Using index condition / Using index merge / Using index(覆盖索引)等ANALYZE TABLE 刷新统计信息或者临时 FORCE INDEX 修复"核心考点定位:理解 MySQL 优化器的成本模型、掌握 EXPLAIN 关键字段、能给出合理的索引优化方案。
知识点拆解:
information_schema.statistics 的 Cardinality(采样估算,可能不准)rows_a = total_rows / cardinality_a,然后回表 rows_a 次过滤 brows_b = total_rows / cardinality_b,然后回表 rows_b 次过滤 arows_a 和 rows_b 里更小的那个WHERE a=xx AND b=xx,优化器可以考虑两个单列索引各自扫描后再合并结果Extra 显示:Using intersect(idx_a, idx_b):结果取交集(AND 场景)Using union(idx_a, idx_b):结果取并集(OR 场景)Using sort_union(...):结果排序后合并WHERE gender='M' AND status=1,扫描 40% + 40% 都超过阈值)key=None + type=ALLANALYZE TABLE t 刷新FORCE INDEX(idx_xxx):强制走USE INDEX(idx_xxx):提示但优化器可以忽略IGNORE INDEX(idx_xxx):忽略某个索引id:查询序号,同一 id 从上到下执行,不同 id 从左到右select_type:SIMPLE / PRIMARY / SUBQUERY / DERIVED 等table:查询的表type:访问类型,从好到坏:system > const > eq_ref > ref > range > index > ALLpossible_keys:可能用到的索引key:实际使用的索引key_len:使用索引的字节数(判断联合索引用到哪几列)rows:预估扫描行数filtered:预估过滤后剩下的百分比Extra:额外信息(Using index 覆盖索引 / Using where 需过滤 / Using index condition ICP / Using filesort 需排序 / Using temporary 需临时表 / Using join buffer 用 join buffer)考察方向:
延伸递进面试题:
EXPLAIN 的 rows 和 filtered,ANALYZE TABLE 刷新统计信息,对比其他索引的实际命中行数,必要时 FORCE INDEX 验证(a, b) 和 (b, a) 有什么区别?(a, b) 对 WHERE a=? AND b=? 最优,对 WHERE b=? 也能用(跳过 a);(b, a) 反过来。通常按查询频率最高的组合建一个就够,第二个查询频率低的话用不上面试官您好,这个问题我按"优化器怎么选 + 为什么这样设计"来答。
先说优化器怎么选。MySQL 是成本模型优化器,它会枚举所有候选方案——走索引 a、走索引 b、走 Index Merge、走全表扫描——然后估算每个方案的成本,选最低的。成本模型的核心是回表次数:走哪个索引,回表过滤另一列的次数最少,优化器就选哪个。所以两个索引区分度差得多时,优化器会选区分度高、扫描行数少的那个;区分度差不多时可能选谁都不一定,具体要看统计信息和当前负载。
Index Merge 是一种理论上的方案,5.6+ 支持。对 WHERE a=xx AND b=xx 优化器可以考虑走两个单列索引分别扫描再取交集(Using intersect)。但实际很少被选中——需要扫两个索引 + 合并结果集,成本通常远高于单个联合索引。除非两个索引都是覆盖索引才可能划算。
推荐建 (a, b) 联合索引是标准答案。因为联合索引一次扫描就能同时定位 a 和 b,理想情况命中就 1 行,回表 1 次;相比之下走单索引最坏要回表成千上万行过滤另一列。频繁一起查的字段一定要建联合索引,这是最本质的优化。
优化器可能完全放弃索引走全表。如果两个索引区分度都低(比如 WHERE gender='M' AND status=1,两个字段各自筛出 40%),扫描占比超过 30% 时优化器会倾向全表扫描,因为 B+ 树随机 IO 不如顺序扫描快。EXPLAIN 显示 key=None type=ALL 就是这种情况。
排查线上优化器选错索引我会按这个流程:先看 EXPLAIN 的 rows 和 filtered 判断是不是选错了;跑 ANALYZE TABLE 刷新统计信息看有没有变化;如果还是选错,FORCE INDEX 强制走指定索引验证效果。FORCE INDEX 生产慎用——索引被删或改结构 SQL 直接生产报错。8.0 的优化器成本模型比 5.7 精准很多,能少踩坑。
当然,具体走哪个索引不能只看索引就断言,一定要用 EXPLAIN 验证。EXPLAIN 里 key 是实际选择的索引、rows 是预估扫描行数、Extra 里的 Using index 说明是覆盖索引、Using filesort 说明需要额外排序——这几个字段基本能定位大部分优化器决策问题。