PostgreSQL/GIN 索引
GIN(广义倒排索引)支持可分为较小组件的数据类型,例如数组的元素、文本文档的单词或 JSON 对象的属性。我们称它们为复合数据类型。与 B 树不同,GIN 不为完整值生成单个索引条目,而是为每个组件生成一个索引条目。
每个索引条目包含单个组件的值加上元组 ID(TID)。请注意,TID 不包含完整值中组件的序列号。它们只包含数据文件中的物理页号加上页中行的号。
WHERE
子句中可用的运算符取决于数据类型。
- 数组:
<@
(包含于),@>
(包含),=
(等于),以及&&
(重叠 / 有一些共同元素)。 - 文本查询(词素):
@@
(包含)。 - JSON:
->
(具有给定键的 JSON 对象字段),->>
(具有给定键的 JSON 对象字段,作为文本)。
-- create a table with a column that holds an array of integers
DROP TABLE IF EXISTS t2;
CREATE TABLE t2 (id INTEGER, arr INTEGER[]);
INSERT INTO t2 VALUES (1, ARRAY [11, 12, 13]);
INSERT INTO t2 VALUES (2, ARRAY [21, 22, 23]);
-- insert a lot of other rows to enforce index usage
INSERT INTO t2 (SELECT generate_series(3, 10000, 1), ARRAY[0, 1, 2, 3]);
-- ------------------------------------------
-- create the GIN index
-- ------------------------------------------
CREATE INDEX t2_gin_idx ON t2 USING GIN(arr);
-- use the index
ANALYSE t2;
EXPLAIN SELECT * FROM t2 WHERE arr @> ARRAY[11];
GIN 索引由不同的子结构组成。
- 元数据页
- 一个条目 B 树
- 一些发布 B 树
- 一个待处理列表
除其他外,元数据页包含指向条目 B 树和待处理列表的指针。
条目 B 树实现了一个树,其中键由原始组件的值组成,例如单个数组元素的值。在非叶级,它们的指针指向下一级的子页。在叶级,页面包含两种类型的条目:首先,有发布列表。它们由键组成,后跟 TID 列表。其次,如果这样的 TID 列表超过了物理页面的容量,该列表将被重新排列为一个发布 B 树,该树存储在一个或多个其他页面上。原始发布列表被指向新的发布 B 树的指针替换。
发布 B 树是部分物理页面的组成部分,并且包含关于元组 ID 的 B 树,这些元组 ID 都指向数据文件中其键可以作为其组件之一的值找到的行。
两种 B 树类型的实现不同于 PostgreSQL 的标准B 树实现。
待处理列表是一个页面列表,其中键(组件的值)及其专用的 TID 按顺序存储。待处理列表的存在是为了优化目的;见下文。
GIN 索引使用两种特殊优化。首先,如果不同组件(可能在不同的行中)的值经常被使用,则 TID 集将被重新排列为 GIN 内部的 B 树。考虑许多文本文档的情况:许多词语很可能在相同和不同的文档中重复使用。在这种情况下,TID 列表可能会增长到数千个,而树比列表更适合管理它们。
其次,复合数据的INSERT
或UPDATE
会创建许多索引条目:每个组件一个,例如文本中的每个词一个。在第一步中,这些新的索引条目被收集在索引树之外的单独待处理列表中(与原始CREATE INDEX
命令相反)。在下一个VACUUM
运行期间,这些条目使用在初始索引创建期间使用的相同批量插入技术从待处理列表移动到 GIN 树结构中。这种批量技术加速了该过程,而且更有用的是,工作被委托给后台进程。当然,也存在缺点:除了遍历索引树之外,每个查询都必须扫描待处理列表。
关于 GIN 的 PostgreSQL 文档
关于 GIN 实现的 PostgreSQL 文档