~码铃薯~

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

联合索引和每个字段各自建立索引的区别(面试有问到)

我在一张表中有a b两个字段,a会进行范围查询,b也会进行范围查询,这个时候我应该怎样建索引,是给a和b两个字段分别建立索引,还是给a和b两个字段建立联合索引。

这个问题不能一概而论,关键看你的查询模式两个范围条件是否同时出现

先记住一个核心原理:

联合索引 (a, b) 中,如果 a 用了范围查询,b 通常不能继续用于索引定位,只能用于索引条件下推(ICP)过滤或覆盖索引。

因为 B+ 树先按 a 排序,a 相同再按 b 排序。当 a 是一个范围时,里面包含多个 a 值,b 只在每个 a 值内部有序,整体不是全局有序的,所以没法直接跳到 b 的范围。


一、分场景判断

场景 1:a 和 b 经常单独作为范围查询

比如:

SELECT * FROM t WHERE a BETWEEN ? AND ?;
SELECT * FROM t WHERE b BETWEEN ? AND ?;

这种情况,分别建单列索引

INDEX idx_a (a);
INDEX idx_b (b);

因为联合索引 (a, b)WHERE b BETWEEN ? 用不上,不满足最左前缀。
分别建索引,优化器可以根据查询选 idx_aidx_b


场景 2:a 和 b 总是同时作为范围条件,用 AND 连接

比如:

SELECT * FROM t 
WHERE a BETWEEN ? AND ? 
  AND b BETWEEN ? AND ?;

这种情况,联合索引 (a, b)(b, a) 可以建,但只能有效利用第一个字段做范围定位,第二个字段只能在索引里过滤,不能用于缩小扫描范围。

所以:

  • 如果 a 的选择性更高,过滤后行数更少,建 (a, b)
  • 如果 b 的选择性更高,建 (b, a)
  • 如果查询需要返回的列很少,可以做成覆盖索引,比如 (a, b, c, d),避免回表。

但不要期望两个范围都走索引定位。


场景 3:一个等值 + 一个范围

比如:

SELECT * FROM t WHERE a = ? AND b BETWEEN ? AND ?;

这时联合索引非常有效:

INDEX idx_a_b (a, b);

a 等值定位,b 范围在 a 内部有序,可以继续用索引范围扫描。

反过来 b = ? AND a BETWEEN ? AND ? 就建 (b, a)


场景 4:OR 连接

比如:

SELECT * FROM t 
WHERE a BETWEEN ? AND ? 
   OR b BETWEEN ? AND ?;

这种情况联合索引基本用不上,分别建单列索引更合适。
MySQL 可能用 index_merge 求并集,但也不一定,性能要看数据量和优化器选择。


二、那到底怎么选?

可以按这个决策:

查询模式 推荐索引
只查 a 范围 idx_a(a)
只查 b 范围 idx_b(b)
a、b 独立范围查询都有 分别建 idx_a(a)idx_b(b)
总是 a AND b 同时范围 (高选择性列, 另一列),第二列用于 ICP/覆盖
a 等值 AND b 范围 (a, b)
b 等值 AND a 范围 (b, a)
a 范围 OR b 范围 分别建单列索引

三、实际建议

  1. 先分别建 idx_a(a)idx_b(b),覆盖大多数独立范围查询。
  2. 如果 a AND b 组合查询很频繁且性能差,再考虑建联合索引 (a, b)(b, a),把过滤后行数少的列放前面。
  3. EXPLAIN 看:
    • key 用了哪个索引;
    • rows 预估扫描行数;
    • filtered 过滤比例;
    • Extra 是否有 Using index conditionUsing whereUsing index
  4. 如果两个范围条件都很宽,比如 a > 0 AND b > 0,索引可能不如全表扫描,优化器也会放弃索引。
  5. 索引不是越多越好,写入、更新、删除都要维护索引,空间也有成本。

四、面试一句话回答

如果 a 和 b 会分别做范围查询,优先分别建单列索引。如果总是 a AND b 同时范围,可以建联合索引,但联合索引里第一个范围列之后,第二个范围列无法用于索引定位,只能 ICP 过滤或覆盖。所以联合索引要把高选择性列放前面,并且不要指望两个范围都走索引。最终用 EXPLAIN 验证。

联合索引(a,b)和联合索引(b,a)能同时创建吗,两者的效果相同吗

能同时创建,但效果不一样,一般也不建议同时建两个。
它们是两个独立的 B+ 树,物理上各占一份存储,写入、更新、删除都要各自维护,成本翻倍。


一、能同时创建吗?

可以。只要索引名不同:

CREATE INDEX idx_ab ON t (a, b);
CREATE INDEX idx_ba ON t (b, a);

MySQL 允许,但这是两个完全不同的索引。
它们不是“同一个索引换个顺序”,而是两棵独立的树。


二、效果相同吗?

不相同。
核心原因是 最左前缀原则:联合索引只能从最左列开始连续使用。

索引 (a, b) 的结构

先按 a 排序,a 相同再按 b 排序。

它能有效支持:

WHERE a = ?                          -- 能用
WHERE a = ? AND b = ?                -- 能用,高效
WHERE a = ? AND b > ?                -- 能用,a 等值 + b 范围
WHERE a > ?                          -- 能用 a 范围,b 不能定位
ORDER BY a, b                        -- 能避免排序

不能有效支持:

WHERE b = ?                          -- 不满足最左前缀,通常用不上
WHERE b > ?                          -- 同上
ORDER BY b, a                        -- 不能避免排序

索引 (b, a) 的结构

先按 b 排序,b 相同再按 a 排序。

它能有效支持:

WHERE b = ?                          -- 能用
WHERE b = ? AND a = ?                -- 能用,高效
WHERE b = ? AND a > ?                -- 能用,b 等值 + a 范围
WHERE b > ?                          -- 能用 b 范围,a 不能定位
ORDER BY b, a                        -- 能避免排序

不能有效支持:

WHERE a = ?                          -- 不满足最左前缀
WHERE a > ?                          -- 同上
ORDER BY a, b                        -- 不能避免排序

三、哪些查询两者效果相同?

对于等值查询,两者通常都能用,效果接近:

WHERE a = ? AND b = ?

因为两个列都等值,(a,b)(b,a) 都能精确定位到组合。
优化器可能选其中一个,具体看统计信息和索引大小,但性能差异通常不大。

对于覆盖索引,如果查询只返回 ab

SELECT a, b FROM t WHERE a = ? AND b = ?;

两个索引都覆盖,都能避免回表。

但只要查询条件或排序涉及“单独用某一列”或“范围列在前”,效果就不同了。


四、举例对比

查询 (a,b) (b,a)
WHERE a = 1 ✅ 高效 ❌ 基本用不上
WHERE b = 1 ❌ 基本用不上 ✅ 高效
WHERE a = 1 AND b = 2 ✅ 高效 ✅ 高效
WHERE a = 1 AND b > 2 ✅ a 等值 + b 范围 ⚠️ 只能用 b 范围,a 过滤
WHERE b = 1 AND a > 2 ⚠️ 只能用 a 范围,b 过滤 ✅ b 等值 + a 范围
WHERE a > 1 AND b > 2 ⚠️ 只能用 a 范围 ⚠️ 只能用 b 范围
ORDER BY a, b ✅ 避免排序 ❌ 需额外排序
ORDER BY b, a ❌ 需额外排序 ✅ 避免排序

五、那要不要同时建两个?

大多数情况不建议。 因为:

  1. 冗余:两个索引包含相同的列,只是顺序不同,占两份空间。
  2. 写放大:每次 INSERT、UPDATE、DELETE 都要维护两棵树,写入性能下降。
  3. 优化器可能选错:索引越多,优化器选择成本越高,有时反而不如少而精。

更合理的做法:

  • 如果 ab 都会单独做范围查询,分别建单列索引:

    INDEX idx_a (a);
    INDEX idx_b (b);
    

    这样比建两个联合索引更轻量,也能覆盖各自的单独查询。

  • 如果经常是 a AND b 组合查询,根据谁的选择性高、谁做等值、谁做范围,建一个联合索引即可:

    -- a 等值 + b 范围
    INDEX idx_a_b (a, b);
    -- 或 b 等值 + a 范围
    INDEX idx_b_a (b, a);
    
  • 如果确实有两种排序需求,比如既要 ORDER BY a,b 又要 ORDER BY b,a,且性能要求高,才考虑建两个联合索引。但也要评估写成本。


六、面试一句话回答

(a,b)(b,a) 可以同时创建,但它们是两个独立的 B+ 树,效果不同。最左前缀决定了 (a,b) 能支持 a 单独查询和 ORDER BY a,b(b,a) 能支持 b 单独查询和 ORDER BY b,a。等值组合查询两者都能用,但单独列查询、范围列前后、排序场景差异明显。一般不建议同时建两个,优先分别建单列索引或选一个联合索引,用 EXPLAIN 验证后再决定。

联合索引(a,b)和给a单独建立索引和给b单独建立索引,效果一样吗

不一样,差别还挺大。
联合索引 (a, b) 和“单列 a 索引 + 单列 b 索引”是两种完全不同的索引方案,支持的查询、排序、覆盖和成本都不同。


一、核心区别一句话

  • 联合索引 (a, b):一棵 B+ 树,先按 a 排序,a 相同再按 b 排序。
  • 单列 a + 单列 b:两棵独立的 B+ 树,一棵按 a 排序,一棵按 b 排序。

所以它们不是“效果一样”,而是各自擅长不同场景


二、支持的查询不同

1. 只查 a

WHERE a = ?
WHERE a > ?
ORDER BY a
  • 联合 (a, b)能用,因为 a 是最左列,相当于一个带 ba 索引。
  • 单列 a能用
  • 效果:大部分情况接近,但联合索引条目更大,可能多占空间、多读页。如果只查 a,单列 a 更轻量。

2. 只查 b

WHERE b = ?
WHERE b > ?
ORDER BY b
  • 联合 (a, b)基本用不上,不满足最左前缀。MySQL 8.0 有索引跳跃扫描,但限制多、不保证,不能当常规方案。
  • 单列 b能用
  • 效果:完全不同,单列 b 完胜。

3. 同时查 a 和 b

WHERE a = ? AND b = ?
  • 联合 (a, b)一次定位到 (a,b) 组合,效率最高。
  • 单列 a + 单列 b:优化器可能用 index_merge 求交集,或者只选其中一个选择性高的索引,再回表过滤另一个。通常不如联合索引稳定高效

4. a 等值 + b 范围

WHERE a = ? AND b > ?
  • 联合 (a, b)a 等值定位,ba 内部有序,可以继续范围扫描,非常高效
  • 单列 a + 单列 b:一般只能用一个索引,另一个回表过滤,效率较低。

5. a 范围 + b 范围

WHERE a > ? AND b > ?
  • 联合 (a, b):只能用 a 做范围定位,b 只能在索引里过滤,不能缩小扫描范围。
  • 单列 a + 单列 b:可能选一个,或 index_merge,但通常也不理想。
  • 两者都不算高效,但联合索引可能减少回表。

6. OR 查询

WHERE a > ? OR b > ?
  • 联合 (a, b)基本用不上
  • 单列 a + 单列 b:可能触发 index_merge 求并集。
  • 这种场景单列索引更合适。

三、排序和覆盖也不同

场景 联合 (a,b) 单列 a + 单列 b
ORDER BY a, b ✅ 可避免排序 ❌ 通常要额外排序
ORDER BY b, a ❌ 要额外排序 ❌ 要额外排序
ORDER BY a ✅ 可避免排序 ✅ 单列 a 可避免
ORDER BY b ❌ 通常不行 ✅ 单列 b 可避免
查询只返回 a, b ✅ 覆盖索引,不回表 ❌ 单列索引不覆盖另一列,要回表
查询只返回 a ✅ 覆盖 ✅ 单列 a 覆盖

四、成本和维护不同

  • 联合 (a,b):一个索引,但包含两列,索引更大。
  • 单列 a + 单列 b:两个索引,总空间可能更大,写操作要维护两棵树,写放大更明显。
  • 如果三个都建(a,b)ab,通常冗余,除非有明确必要,否则不推荐。

五、怎么选?

看查询模式:

查询模式 推荐
只查 a 单列 a,或联合 (a,b) 也能用
只查 b 必须单列 b
ab 都会单独范围查 分别建单列 a、单列 b
经常 a = ? AND b = ? 联合 (a,b)
经常 a = ? AND b > ? 联合 (a,b)
经常 b = ? AND a > ? 联合 (b,a)
a 单独查多,b 单独查少 联合 (a,b) 替代 a 单列,再按需建 b
a OR b 分别建单列 a、单列 b

六、面试一句话回答

不一样。联合索引 (a,b) 是一棵树,能高效支持 a 单独查询和 a AND b 组合查询,但基本不支持 b 单独查询。单列 a + 单列 b 是两棵树,能分别支持 ab 的单独查询,但组合查询通常不如联合索引高效,可能走 index_merge 或只选一个索引回表过滤。所以要根据实际查询模式选,不能简单说谁替代谁。

posted on 2026-09-15 17:29  ~码铃薯~  阅读(7)  评论(0)    收藏  举报