在内部,一个GIN索引包含一个在键上构建的 B 树索引,其中每一个键是一个或者多个被索引项的一个元素(例如,数组的一个成员),并且叶子页中的每一个元组包含一个指向堆指针 B 树的指针(一个“位置树”)或者一个堆指针的简单列表(“位置列表”),只有位置列表小到能够和键值一起放入索引时才使用后一种形式。 图 66.1举例说明了这些GIN索引的组件。
自PostgreSQL 9.1 起,空键值可以被包括在索引中。同样,用于为空或者根据extractValue
不包含键的被索引项的占位符空值也被包括在索引中。这允许实现应该找到空项的搜索。
多列GIN索引可以通过在组合值(列号,键值)上建立一个单一 B 树实现。不同列的键值可以是不同类型。
图 66.1. GIN内部
更新一个GIN索引可能会比较慢,这是因为倒排索引的天然特性造成的:
对一个堆行的插入或更新可能导致对索引的很多次插入(每一次插入用于从被索引项中抽取的一个键)。
从PostgreSQL 8.4 开始,GIN可以通过将新元组插入到一个临时的未排序的待处理条目列表中来推迟很多这种工作。
当表被清理、自动分析、gin_clean_pending_list
函数被调用
或者待处理列表变得大于gin_pending_list_limit时,
这些条目被使用初始索引创建时使用的批量插入技术移动到主GIN数据结构中。
这大幅度提高了GIN索引的更新速度,虽然带了一些额外的清理负荷。
此外,这些开销可以通过用一个后台进程来取代一个前台进程执行。
这种方式的主要缺点是搜索必须在搜索普通索引之外扫描待处理条目的列表,并且因此一个大型的待处理条目列表会显著地拖慢搜索。另一个缺点是,虽然大部分更新变快了,一次导致待处理列表变得“太大”的更新将导致一次立即清理循环并且因此会比其他更新慢很多。正确使用自动清理可以把这些问题的影响变得最小。
如果一致的响应时间比更新速度更重要,可以通过为一个GIN关闭fastupdate
存储参数来禁用对待处理条目的使用。详见CREATE INDEX。
GIN 可以支持“部分匹配”查询,在其中查询不能判断一个或多个键的精确匹配,但是可以确定落在键值(在compare
支持方法决定的键排序顺序中)的一个合理的狭窄范围内的可能匹配。extractQuery
方法,不会返回一个要被精确匹配的键值,而是返回一个作为要被搜索范围下界的键值,并且将pmatch
标志设置为真。然后键范围将被使用comparePartial
方法扫描。comparePartial
必须对于一个匹配的索引键返回零,对一个不匹配但仍在要被搜索的范围内的返回小于零,对于超过被搜索范围的索引键返回大于零。