联合索引几乎是数据库面试的必问题。很多回答会直接背“最左前缀原则”,然后举一个 a、b、c 三列索引的例子。这个回答不算错,但如果停在规则层面,很容易被继续追问:范围查询之后还能不能用后面的列?order by 为什么有时能用索引有时不能?索引建得越多是不是越好?
联合索引真正要讲的是数据库如何利用有序结构减少扫描。把它理解成一串口令,容易答死;把它理解成 B+Tree 上按多列顺序排列的数据,很多现象就自然了。
最左原则来自排序方式
如果有联合索引 (a, b, c),索引项先按 a 排,再在 a 相同的范围内按 b 排,最后在 a、b 都相同的范围内按 c 排。数据库能高效定位的前提,是查询条件能沿着这个顺序逐步缩小范围。
所以 where b = ? 在没有 a 条件时通常很难直接利用这个联合索引做精确定位,因为全局上 b 并不是连续有序的。where a = ? and b = ? 则能逐层缩小范围。这个解释比背“必须从最左开始”更有说服力。
范围查询会改变后续列的作用
当查询里出现 a = ? and b > ? and c = ? 时,b 已经变成一个范围。数据库可以利用 a 和 b 定位一段区间,但 c 在这段区间里不再是全局连续的精确定位条件。不同版本和优化器策略下,后续列可能用于过滤或索引条件下推,但不能简单说它和等值条件一样继续缩小搜索边界。
面试里更稳的说法是:范围条件之后的列是否还能减少回表和过滤成本,要看执行计划,不能只用口诀判断。
排序和覆盖索引常被忽略
联合索引不只服务 where,也可能服务 order by。如果查询过滤和排序方向能匹配索引顺序,数据库可以少做一次额外排序。反过来,如果排序列顺序不一致、混合方向不合适,或者前面的过滤条件没有固定住,仍可能需要 filesort。
覆盖索引也很实用。如果查询需要的列都在索引里,数据库可以直接从二级索引拿到结果,减少回表。列表页、分页查询、轻量搜索经常靠这个优化。但覆盖索引不是免费午餐,索引列越多,占用空间越大,写入维护成本也越高。
建索引要从业务查询开始
项目里最糟糕的索引设计,是看到慢 SQL 就把所有 where 列都塞进一个联合索引。正确顺序应该是先看业务查询形态:等值条件有哪些,范围条件有哪些,排序和分页怎么做,返回列是否可以覆盖,数据分布是否有选择性。
比如状态字段只有少量枚举值,单独建索引可能选择性很差;但如果它和租户 ID、创建时间组合在一起,用于查询某个租户下某类订单的时间范围,就可能很有价值。索引价值来自组合后的访问路径,而不是某一列看起来重要。
面试里最该补的一句话
联合索引不是背规则,而是验证假设。你可以先根据列顺序、等值和范围条件推测索引如何使用,再用 EXPLAIN、慢查询日志和真实数据分布验证。如果线上数据量、选择性和查询条件变化,原来合理的索引也可能变差。
能从 B+Tree 顺序讲到执行计划、覆盖索引、排序和写入代价,说明你不是只会背最左原则,而是在用数据库的方式思考查询。