联合索引和每个字段各自建立索引的区别(面试有问到)
我在一张表中有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_a 或 idx_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 范围 |
分别建单列索引 |
三、实际建议
- 先分别建
idx_a(a)和idx_b(b),覆盖大多数独立范围查询。 - 如果
a AND b组合查询很频繁且性能差,再考虑建联合索引(a, b)或(b, a),把过滤后行数少的列放前面。 - 用
EXPLAIN看:key用了哪个索引;rows预估扫描行数;filtered过滤比例;Extra是否有Using index condition、Using where、Using index。
- 如果两个范围条件都很宽,比如
a > 0 AND b > 0,索引可能不如全表扫描,优化器也会放弃索引。 - 索引不是越多越好,写入、更新、删除都要维护索引,空间也有成本。
四、面试一句话回答
如果 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) 都能精确定位到组合。
优化器可能选其中一个,具体看统计信息和索引大小,但性能差异通常不大。
对于覆盖索引,如果查询只返回 a 和 b:
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 |
❌ 需额外排序 | ✅ 避免排序 |
五、那要不要同时建两个?
大多数情况不建议。 因为:
- 冗余:两个索引包含相同的列,只是顺序不同,占两份空间。
- 写放大:每次 INSERT、UPDATE、DELETE 都要维护两棵树,写入性能下降。
- 优化器可能选错:索引越多,优化器选择成本越高,有时反而不如少而精。
更合理的做法:
-
如果
a和b都会单独做范围查询,分别建单列索引: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是最左列,相当于一个带b的a索引。 - 单列
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等值定位,b在a内部有序,可以继续范围扫描,非常高效。 - 单列
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)、a、b,通常冗余,除非有明确必要,否则不推荐。
五、怎么选?
看查询模式:
| 查询模式 | 推荐 |
|---|---|
只查 a |
单列 a,或联合 (a,b) 也能用 |
只查 b |
必须单列 b |
a 和 b 都会单独范围查 |
分别建单列 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是两棵树,能分别支持a和b的单独查询,但组合查询通常不如联合索引高效,可能走index_merge或只选一个索引回表过滤。所以要根据实际查询模式选,不能简单说谁替代谁。
浙公网安备 33010602011771号