受支持版本: 当前版本 (18) / 17 / 16 / 15 / 14
开发版本: devel

11.3. 多列索引 #

一个索引可以定义在表的多个列上。例如,如果你有一个如下形式的表:

CREATE TABLE test2 (
  major int,
  minor int,
  name varchar
);

(假设你把/dev目录保存在数据库里……)并且经常发出如下查询:

SELECT name FROM test2 WHERE major = constant AND minor = constant;

那么将majorminor两列一起建一个索引可能是合适的,例如:

CREATE INDEX test2_mm_idx ON test2 (major, minor);

当前,只有 B-tree、GiST、GIN 和 BRIN 索引类型支持多键列索引。能否有多个键列,与索引中能否添加INCLUDE列无关。索引最多可以有 32 列,包括INCLUDE列。(这个限制在构建PostgreSQL时可以修改;参见文件pg_config_manual.h。)

多列 B-树索引可以用于涉及索引任意列子集的查询条件,但当对前导(最左)列存在约束时,索引效率最高。精确的规则是:对前导列的等值约束,再加上第一个没有等值约束列上的任意不等约束,总会被用来限制被扫描的索引范围。这些列右侧列上的约束会在索引中进行检查,因此总能减少访问表本体的次数,但不一定会减少必须扫描的索引范围。如果 B-树索引扫描能够有效应用跳过扫描优化,那么它在通过反复的索引搜索遍历索引时,会利用每一列约束。这可能减少需要读取的索引部分,即便一个或多个列(位于查询谓词中最低有效索引列之前)缺少常规的等值约束。跳过扫描的工作方式是内部生成一个动态的等值约束,使其匹配索引列中的每个可能值(不过这只适用于某个列缺少来自查询谓词的等值约束,且生成的约束能够与查询谓词中较后列的约束联合使用时)。

例如,给定一个(x, y)上的索引和查询条件WHERE y = 7700,B-树索引扫描可能能够应用跳过扫描优化。这通常发生在查询规划器预计:针对每个可能的N反复执行WHERE x = N AND y = 7700搜索(或者针对索引中实际存储的每个x值),在当前表上可用索引的前提下,这是最快的方法。一般来说,只有当不同的x值很少,以致规划器预计扫描能跳过索引的大部分内容时,才会采用这种方法(因为大多数叶子页都不可能包含相关元组)。如果不同的x值很多,那么就必须扫描整个索引,因此在大多数情况下,规划器会更倾向于顺序扫描表而不是使用该索引。

在至少具有一部分来自查询谓词的有用约束的 B-树扫描中,跳过扫描优化也可以被选择性地应用。例如,给定一个(a, b, c)上的索引和查询条件WHERE a = 5 AND b >= 42 AND c < 77,该索引可能必须从第一个a = 5 且b = 42 的条目扫描,一直到最后一个a = 5 的条目。c >= 77 的索引项永远不需要在表层面进行过滤,但是否值得在索引内部跳过它们,则未必。发生跳过时,扫描会启动一次新的索引搜索,把自身从当前a = 5 且b = N 分组的末尾重新定位(也就是索引中第一个a = 5 AND b = N AND c >= 77元组出现的位置),移动到下一个这类分组的起始位置(也就是索引中第一个a = 5 AND b = N + 1元组出现的位置)。

多列 GiST 索引可以用于涉及索引任意列子集的查询条件。附加列上的条件会限制索引返回的项,但决定索引需要扫描多少内容的,最重要的仍是第一列上的条件。如果第一列只有很少几个不同值,即使其他列有很多不同值,GiST 索引也会相对低效。

多列 GIN 索引可以用于涉及索引任意列子集的查询条件。与 B-树或 GiST 不同,无论查询条件使用的是哪一列索引列,GIN 的索引搜索效果都一样。

多列 BRIN 索引可以用于涉及索引任意列子集的查询条件。和 GIN 一样、不同于 B-树或 GiST,无论查询条件使用的是哪一列索引列,索引搜索效果都一样。在单个表上使用多个 BRIN 索引,而不是使用一个多列 BRIN 索引的唯一理由,是需要不同的pages_per_range存储参数。

当然,每一列都必须配合适合该索引类型的操作符使用;涉及其他操作符的子句不会被考虑。

多列索引应谨慎使用。在大多数情况下,单列索引已经足够,而且更省空间、也更省时间。除非表的使用方式极其程式化,否则超过三列的索引通常不会有帮助。关于不同索引配置优缺点的讨论,还可参见Section 11.5Section 11.9

提交更正

如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。