57.3. 实现
在内部,GIN索引包含一个在键上构造的B-tree索引, 每个键是一个或多个被索引项目的一个元素(比如,一个数组的一个成员) 。 并且叶子页上每个元组包含了或者堆指针的B-tree的一个指针(一个"posting tree"), 或者,当列表小到足以和键值一起存储到一个索引元组中时,则是堆指针的一个简单列表(一个"posting list"),
从PostgreSQL 9.1开始,NULL的键值可以被包含到索引里。 对NULL的或根据extractValue
不包含任何键的被索引项,占位符null被包含到了索引中。 这就允许应该找到空项目的搜索可以执行。
多列GIN索引通过在组合值(列号,键值)上建立一个单个的B-tree实现。 不同列的键值可以有不同的类型。
57.3.1. GIN快速更新技术
由于倒排索引的本质特性,更新一个GIN索引可能会比较慢。 插入或更新一个堆行可能导致许多往索引的插入(从被索引项目中抽取出的每一个键一个)。 从PostgreSQL 8.4开始, GIN可以通过插入新的元组到一个临时的,待处理实体的未排序列表,来推迟很多这样的工作。 当表被vacuumed,或者如果待处理实体的列表太大了(大于work_mem), 这些实体被使用和初始索引创建时用到的相同的bulk插入方法,移动到主要的GIN数据结构。 即使把额外的vacuum开销算进去,这也大大提升了GIN索引更新的速度。 而且,这种额外开销的工作可以通过后台进程而不是前端查询来处理。
这种方法的主要缺点在于搜索时除了常规的索引还必须要扫描待处理实体的列表。 因此,大的待处理实体的列表会显著的拖慢搜索。 另一个缺点是,虽然大多数更新很快,一个导致待处理列表(pending list)变得"太大"的更新将引发一个立即的清理周期, 并因此比起其它更新会非常慢。 恰当的使用autovacuum可以最小化这两个问题。
如果一致的响应时间比更新速度更重要,可以通过把GIN 索引的存储参数FASTUPDATE
设置为off而不使用待处理实体。 详细请参考CREATE INDEX。
57.3.2. 部分匹配算法
GIN可以支持"部分匹配"查询。即:查询并不决定单个或多个键的一个精确的匹配, 而是,可能的匹配落在一个合理的狭窄键值范围内(根据compare
支持函数决定的键值排序顺序)。 此时,extractQuery
方法并不返回一个用于精确匹配的键值,取而代之的是, 返回一个要被搜索的键值范围的下边界,并且设置pmatch
为true。 然后,这个键值范围被使用comparePartial
进行扫描。 comparePartial
必须为一个相匹配的索引键返回0,不匹配但依然在被搜索范围内时返回小于0的值,对超过可以匹配的范围的索引键则返回大于0的值。