在内部,一个 GIN 索引包含一个基于键构建的 B-树索引,其中每个键都是一个或多个被索引项中的某个元素 (例如数组成员),而叶子页中的每个元组要么包含一个指向堆指针 B-树的指针(“posting tree”), 要么在列表足够小、能够连同键值一起放入单个索引元组时,直接包含一个简单的堆指针列表(“posting list”)。 Figure 65.1 展示了 GIN 索引的这些组成部分。
从 PostgreSQL 9.1 开始,索引中可以包含 null 键值。 此外,对于那些自身为 null,或按照 extractValue 的定义不包含任何键的被索引项, 索引中也会包含占位符 null 值。这样一来,应该找到空项的搜索就能够找到它们。
多列 GIN 索引的实现方式,是在组合值(列号,键值)之上构建一个单独的 B-树。 不同列的键值可以具有不同类型。
Figure 65.1. GIN 内部结构
由于倒排索引的内在性质,更新 GIN 索引往往比较慢:插入或更新一个堆元组, 可能会导致向索引中执行多次插入(从被索引项中提取出的每个键都要插入一次)。 GIN 可以通过把新元组插入一个临时的、未排序的待处理列表, 来推迟其中的大部分工作。当表被清理或自动分析时,或者调用 gin_clean_pending_list 函数时,又或者待处理列表增长到大于 gin_pending_list_limit 时,这些项就会被移入主要的 GIN 数据结构中,所用的是与初始索引创建时相同的批量插入技术。 即便把额外的清理开销计算在内,这也会大幅提高 GIN 索引的更新速度。 此外,这部分开销工作还可以由后台进程完成,而不是在前台查询处理过程中完成。
这种方法的主要缺点是,搜索除了要查找常规索引之外,还必须扫描待处理列表, 因此大型待处理列表会显著拖慢搜索。另一个缺点是,虽然大多数更新都很快, 但一旦某次更新使待处理列表变得“过大”,就会立刻触发一次清理周期, 因此该次更新会比其他更新慢得多。恰当地使用自动清理可以将这两个问题都尽量减轻。
如果一致的响应时间比更新速度更重要,可以通过关闭 GIN 索引的 fastupdate 存储参数来禁用待处理列表机制。详见 CREATE INDEX。
GIN 可以支持“部分匹配”查询。在这种查询中, 查询无法确定一个或多个键的精确匹配,但可能的匹配会落在一个相对狭窄的键值范围内 (该范围位于由 compare 支持方法确定的键排序顺序中)。 extractQuery 方法不是返回一个要精确匹配的键值, 而是返回待搜索范围的下界键值,并将 pmatch 标志设为 true。 随后使用 comparePartial 方法扫描该键范围。 对于匹配的索引键,comparePartial 必须返回零; 对于虽然不匹配但仍处在待搜索范围内的索引键,返回小于零; 如果索引键已经超出了可能匹配的范围,则返回大于零。
如果您发现文档中有不正确的内容、与您使用特定功能的经验不符或需要进一步说明,请使用此表单来报告文档问题。