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