Postgresql Gist索引

Gist是generalized search tree的缩写,是一棵平衡的搜索树,和之前介绍的btree类似。
那么它们有什么区别? «btree»索引:能够支持大于,小于和等于运算符。但是,当数据库存储的数据是地理数据,文本文档,图像等数据的时候,btree是无效的。

Gist索引结构:

GiST是由节点页面组成的高度平衡树。节点由索引行组成。

通常,叶子节点的每一行(叶行)都包含一些谓词(布尔表达式)和对表行(TID)的引用。索引键必须符合这些谓词。

就像普通的Btree搜索一样,搜索从根节点开始。一致性函数查找有在范围内的子节点。然后对找到的子节点重复该算法。如果节点是叶子节点,则由一致性函数选择该行作为结果返回。

搜索是深度优先的:算法首先尝试到达叶子节点。这样就可以尽可能的第一时间返回结果。
此外,以下情况可能使用Gist更加有效:

  1. 点(其它几何体)和最近相邻的搜索。
  2. 间隔和排除约束。
  3. 全文搜索。
R-tree for point

为了更好的理解上面所说的索引结构,我们将通过一个平面中的点的索引示例来说明上述内容。常规的B树不适合这种数据类型的数据,因为没有为点定义比较运算符。

R-tree的原理是将平面划分为多个矩形,这些矩形覆盖所有索引的点。索引行存储一个矩形,可以这样定义谓词:“在给定的矩形内搜索点是否存在”。
R-tree的根将包含几个最大的矩形(可能相交)。子节点将包含较小尺寸的矩形,这些矩形嵌入到父节点中,并且覆盖所有的点。

第一级:可见两个大的矩形相交

在这里插入图片描述

第二级:将大矩形分割成较小的区域。
在这里插入图片描述
第三级:每个矩形包含一些点的位置存储到一个索引页。
在这里插入图片描述

现在我们看一个简单的点的示例:
在这里插入图片描述

postgres=# create table points(p point);

postgres=# insert into points(p) values
  (point '(1,1)'), (point '(3,2)'), (point '(6,3)'),
  (point '(5,5)'), (point '(7,8)'), (point '(8,6)');

postgres=# create index on points using gist(p);

通过拆分,索引结构如下:
在这里插入图片描述

创建的索引可用于加快以下查询的速度,例如:“查找给定矩形中包含的所有点”。可以将这种条件形式化如下:p <@ box ‘(2,1),(6,3)’ (操作符 <@ 意思包含于):

postgres=# set enable_seqscan = off;

postgres=# explain(costs off) select * from points where p <@ box '(2,1),(7,4)';
                  QUERY PLAN                  
----------------------------------------------
 Index Only Scan using points_p_idx on points
   Index Cond: (p <@ '(7,4),(2,1)'::box)
(2 rows)

“indexed-field <@ expression”,这里indexed-field是一个点, expression是一个矩形,如果索引的矩形和给的表达式矩形相交,则返回yes,对于叶子行,如果这个点与表达式的矩形相交,也就是包含,那么返回yes.

在这里插入图片描述
搜索从根节点开始。矩形(2,1)-(7,4)与(1,1)-(6,3)相交,但不与(5,5)-(8,8)相交,因此,不需要下降到第二个子树。
在这里插入图片描述

到达叶节点后,我们查找了包含的三个点,并返回符合条件的两个点:(3,2)和(6,3)

postgres=# select * from points where p <@ box '(2,1),(7,4)';
   p  
-------
 (3,2)
 (6,3)
(2 rows)
内部结构

pageinspect不支持查看Gist索引的内部信息,但是另一个插件Gevel可以查看内部信息。
安装过程:http://www.sai.msu.su/~megera/wiki/Gevel
安装后,可以使用三个函数,首先,我们查看一下统计信息:

postgres=# select * from gist_stat('airports_coordinates_idx');
                gist_stat                
------------------------------------------
 Number of levels:          4            +
 Number of pages:           690          +
 Number of leaf pages:      625          +
 Number of tuples:          7873         +
 Number of invalid tuples:  0            +
 Number of leaf tuples:     7184         +
 Total size of tuples:      354692 bytes +
 Total size of leaf tuples: 323596 bytes +
 Total size of index:       5652480 bytes+
 
(1 row)

显然,该索引的大小为690个page,索引分为四级,叶子节点625个page,可以判断第四级节点是叶子节点。

其次,我们可以查看索引树

postgres=# select * from gist_tree('airports_coordinates_idx');
                                       gist_tree                                              
-----------------------------------------------------------------------------------------
 0(l:0) blk: 0 numTuple: 5 free: 7928b(2.84%) rightlink:4294967295 (InvalidBlockNumber) +
     1(l:1) blk: 335 numTuple: 15 free: 7488b(8.24%) rightlink:220 (OK)                 +
         1(l:2) blk: 128 numTuple: 9 free: 7752b(5.00%) rightlink:49 (OK)               +
             1(l:3) blk: 57 numTuple: 12 free: 7620b(6.62%) rightlink:35 (OK)           +
             2(l:3) blk: 62 numTuple: 9 free: 7752b(5.00%) rightlink:57 (OK)            +
             3(l:3) blk: 72 numTuple: 7 free: 7840b(3.92%) rightlink:23 (OK)            +
             4(l:3) blk: 115 numTuple: 17 free: 7400b(9.31%) rightlink:33 (OK)          +
 ...

第三,我们查看存储在索引行的数据,可以看到索引行存储的都是box类型的数据。

postgres=# select level, a from gist_print('airports_coordinates_idx')
  as t(level int, valid bool, a box) where level = 1;
 level |                                   a                                  
-------+-----------------------------------------------------------------------
     1 | (47.663586,80.803207),(-39.2938003540039,-90)
     1 | (179.951004028,15.6700000762939),(15.2428998947144,-77.9634017944336)
     1 | (177.740997314453,73.5178070068359),(15.0664,10.57970047)
     1 | (-77.3191986083984,79.9946975708),(-179.876998901,-43.810001373291)
     1 | (-39.864200592041,82.5177993774),(-81.254096984863,-64.2382965088)
(5 rows)
搜索和排序操作:

到目前为止讨论的运算符(例如 谓词<@ , p <@ box ‘(2,1),(7,4)’)可以称为搜索运算符,因为它们在查询中指定了搜索条件。
还有另一种运算符类型:排序运算符。它们用于ORDER BY子句中。示例如下:

postgres=# select * from points order by p <-> point '(4,7)' limit 2;
   p  
-------
 (5,5)
 (7,8)
(2 rows)

p <-> point ‘(4,7)’,是一个使用排序运算符<->的表达式,它表示从给定一个参数到另一个参数的距离。以上查询的意思是返回最接近点(4,7)的两个点。这样的搜索称为k-NN,k最近邻居搜索。

为了支持这种查询,必须定义一个附加的距离函数,并且排序运算符必须包含在支持运算符类中(例如,点的«points_ops»类)。以下查询显示了运算符及其类型(«s»—搜索和«o»—顺序):

select amop.amopopr::regoperator, amop.amoppurpose, amop.amopstrategy
from pg_opclass opc, pg_opfamily opf, pg_am am, pg_amop amop
where opc.opcname = 'point_ops'
and opf.oid = opc.opcfamily
and am.oid = opf.opfmethod
and amop.amopfamily = opc.opcfamily
and am.amname = 'gist'
and amop.amoplefttype = opc.opcintype;
      amopopr      | amoppurpose | amopstrategy
-------------------+-------------+--------------
 <<(point,point)   | s           |            1  strictly left
 >>(point,point)   | s           |            5  strictly right
 ~=(point,point)   | s           |            6  coincides
 <^(point,point)   | s           |           10  strictly below
 >^(point,point)   | s           |           11  strictly above
 <->(point,point)  | o           |           15  distance
 <@(point,box)     | s           |           28  contained in rectangle
 <@(point,polygon) | s           |           48  contained in polygon
 <@(point,circle)  | s           |           68  contained in circle
(9 rows)

让我们图示来解释说明一下:

select * from points order by p <-> point '(4,7)' limit 2;

在这里插入图片描述
搜索从根节点开始。该节点包含两个边界矩形。到(1,1)-(6,3)的距离是4.0,到(5,5)-(8,8)的距离是1.0。

按照距离的远近顺序遍历子节点。这样,我们首先下降到最近的子节点并计算到这些点的距离(我们将在图中显示数字以提高可见性):
在这里插入图片描述
通过上图可以看到要返回前两个点(5,5)和(7,8)。由于我们知道到矩形(1,1)-(6,3)内的点的距离为4.0或更大,因此我们不必下降到第一个子节点。

但是,如果我们要找距离最近的三个节点

postgres=# select * from points order by p <-> point '(4,7)' limit 3;
   p  
-------
 (5,5)
 (7,8)
 (8,6)

尽管第二个子节点包含所有点,但是在没有查看第一个节点之前,我们无法返回(8,6),因为第一个节点包含更近的点(因为4.0 <4.1)
在这里插入图片描述
由上图可以说明,我们需要查询第一个节点,虽然这样扫描的行多了,降低了效率,但是保证了结果的正确性。

时间间隔的R-tree

使用GiST索引访问方法的另一个示例是间隔的索引,例如时间间隔(“ tsrange”类型)。所有不同之处在于内部节点将包含边界间隔,而不是边界矩形。

让我们举一个简单的例子。我们将出租一间小屋并将预订的时间段存储在表中:

postgres=# create table reservations(during tsrange);

postgres=# insert into reservations(during) values
('[2016-12-30, 2017-01-09)'),
('[2017-02-23, 2017-02-27)'),
('[2017-04-29, 2017-05-02)');

postgres=# create index on reservations using gist(during);

索引可以加速以下查询语句的速度

postgres=# select * from reservations where during && '[2017-01-01, 2017-04-01)';
                    during                    
-----------------------------------------------
 ["2016-12-30 00:00:00","2017-01-08 00:00:00")
 ["2017-02-23 00:00:00","2017-02-26 00:00:00")
(2 rows)

postgres=# explain (costs off) select * from reservations where during && '[2017-01-01, 2017-04-01)';
                                     QUERY PLAN                                    
------------------------------------------------------------------------------------
 Index Only Scan using reservations_during_idx on reservations
   Index Cond: (during && '["2017-01-01 00:00:00","2017-04-01 00:00:00")'::tsrange)
(2 rows)

&&区间运算符表示交集;因此,查询必须返回与给定间隔相交的所有间隔。对于这种运算符,一致性函数确定给定的间隔是与内部的行还是叶子的行相交。

注意,尽管为间隔定义了比较运算符,但这并不是以一定顺序获取间隔。我们也可以为间隔使用«btree»索引,但是B-tree不支持以下的操作(除了等值查询):

postgres=# select amop.amopopr::regoperator, amop.amoppurpose, amop.amopstrategy
from pg_opclass opc, pg_opfamily opf, pg_am am, pg_amop amop
where opc.opcname = 'range_ops'
and opf.oid = opc.opcfamily
and am.oid = opf.opfmethod
and amop.amopfamily = opc.opcfamily
and am.amname = 'gist'
and amop.amoplefttype = opc.opcintype;
         amopopr         | amoppurpose | amopstrategy
-------------------------+-------------+-------------- 
 @>(anyrange,anyelement) | s           |           16  contains element
 <<(anyrange,anyrange)   | s           |            1  strictly left
 &<(anyrange,anyrange)   | s           |            2  not beyond right boundary
 &&(anyrange,anyrange)   | s           |            3  intersects
 &>(anyrange,anyrange)   | s           |            4  not beyond left boundary
 >>(anyrange,anyrange)   | s           |            5  strictly right
 -|-(anyrange,anyrange)  | s           |            6  adjacent
 @>(anyrange,anyrange)   | s           |            7  contains interval
 <@(anyrange,anyrange)   | s           |            8  contained in interval
 =(anyrange,anyrange)    | s           |           18  equals
(10 rows)
内部信息:
postgres=# select level, a from gist_print('reservations_during_idx')
as t(level int, valid bool, a tsrange);
 level |                       a                      
-------+-----------------------------------------------
     1 | ["2016-12-30 00:00:00","2017-01-09 00:00:00")
     1 | ["2017-02-23 00:00:00","2017-02-27 00:00:00")
     1 | ["2017-04-29 00:00:00","2017-05-02 00:00:00")
(3 rows)
排它约束

Gist支持排它约束
排它约束确保表中任何两行相关字段的唯一性。如果使用“=”运算符,因为是唯一约束,所以任意两行的给定字段都不会相等。

一个使用排除约束的示例。不允许插入相交的间隔。

postgres=# alter table reservations add exclude using gist(during with &&);

创建排它约束后,插入一行

postgres=# insert into reservations(during) values ('[2017-06-10, 2017-06-13)');

再插入相交的一行,会报由于排它约束,无法插入

postgres=# insert into reservations(during) values ('[2017-05-15, 2017-06-15)');
ERROR: conflicting key value violates exclusion constraint "reservations_during_excl"
DETAIL: Key (during)=(["2017-05-15 00:00:00","2017-06-15 00:00:00")) conflicts with existing key (during)=(["2017-06-10 00:00:00","2017-06-13 00:00:00")).

可以插入不相交的记录

 insert into reservations(during) values ('[2017-06-30, 2017-07-30]');
«btree_gist» 插件

这里接上面的例子,比如我们要出租多间房屋,以下添加一个房号字段。

postgres=# alter table reservations add house_no integer default 1;

这里我们删除掉排它约束,由于Gist不支持int类型的=号操作符,所以这里会报错

postgres=# alter table reservations drop constraint reservations_during_excl;

postgres=# alter table reservations add exclude using gist(during with &&, house_no with =);
ERROR: data type integer has no default operator class for access method "gist"
HINT: You must specify an operator class for the index or define a default operator class for the data type.

这里我们需要安装btree_gist插件来支持相关操作

postgres=# create extension btree_gist;

postgres=# alter table reservations add exclude using gist(during with &&, house_no with =);

但是,我们现在仍然无法在同一个日期上预定同一间屋子

postgres=# insert into reservations(during, house_no) values ('[2017-05-15, 2017-06-15)', 1);
ERROR: conflicting key value violates exclusion constraint "reservations_during_house_no_excl"

可是,我们可以预定编号为2的房间

postgres=# insert into reservations(during, house_no) values ('[2017-05-15, 2017-06-15)', 2);

注意,尽管GiST索引可以以某种方式支持“大于”,“小于”和“等于”操作运算符,但是B-tree在这中操作中可能会更好。因此,可以考虑在以上的实例中再使用GiST索引的相关技术。

RD-tree 全文搜索

简单介绍一下全文搜索,全文搜索即从文档集中选择与搜索查询匹配的那些文档。如果有很多匹配的文档,如果最快速的匹配将显得十分重要,因为这样可以提高查询效率。
所以,出于搜索目的,文档被转换为特殊类型«tsvector»,其中包含词数及其在文档中的位置。词数是转换为适合搜索形式的单词。例如,单词通常会转换为小写字母,并且变量的结尾会被截断:

postgres=# select to_tsvector('There was a crooked man, and he walked a crooked mile');
               to_tsvector               
-----------------------------------------
 'crook':4,10 'man':5 'mile':11 'walk':8
(1 row)

可以看到,有些单词背丢弃掉了,比如«there», «was», «a», «and», «he»,可能是因为出现的太频繁,失去了搜索他们的意义。当然也可以配置不丢弃这些单词,但是这又是另外一回事了,这里先不介绍。

搜索查询的另一种类型«tsquery»,简单来说,一个查询由一个或几个连接词连接的词素组成:«和»&,«或»|,«not»!…我们也可以使用括号来阐明操作优先级。

postgres=# select to_tsquery('man & (walking | running)');
         to_tsquery         
----------------------------
 'man' & ( 'walk' | 'run' )
(1 row)

全文搜索中只能使用@@符号来匹配

postgres=# select to_tsvector('There was a crooked man, and he walked a crooked mile') @@ to_tsquery('man & (walking | running)');
 ?column?
----------
 t
(1 row)
select to_tsvector('There was a crooked man, and he walked a crooked mile') @@ to_tsquery('man & (going | running)');
 ?column? 
----------
 f
(1 row)
RD-trees

为了进行快速的全文搜索,首先,该表需要一列“ tsvector”类型的列(避免每次搜索时都进行代价昂贵的转换),其次,必须在该列上建立索引。 这里我们使用GiST索引举例。

postgres=# create table ts(doc text, doc_tsv tsvector);

postgres=# create index on ts using gist(doc_tsv);

postgres=# insert into ts(doc) values
  ('Can a sheet slitter slit sheets?'), 
  ('How many sheets could a sheet slitter slit?'),
  ('I slit a sheet, a sheet I slit.'),
  ('Upon a slitted sheet I sit.'), 
  ('Whoever slit the sheets is a good sheet slitter.'), 
  ('I am a sheet slitter.'),
  ('I slit sheets.'),
  ('I am the sleekest sheet slitter that ever slit sheets.'),
  ('She slits the sheet she sits on.');

postgres=# update ts set doc_tsv = to_tsvector(doc);

转换文本到tsvector类型

postgres=# select * from ts;
-[ RECORD 1 ]----------------------------------------------------
doc     | Can a sheet slitter slit sheets?
doc_tsv | 'sheet':3,6 'slit':5 'slitter':4
-[ RECORD 2 ]----------------------------------------------------
doc     | How many sheets could a sheet slitter slit?
doc_tsv | 'could':4 'mani':2 'sheet':3,6 'slit':8 'slitter':7
-[ RECORD 3 ]----------------------------------------------------
doc     | I slit a sheet, a sheet I slit.
doc_tsv | 'sheet':4,6 'slit':2,8
-[ RECORD 4 ]----------------------------------------------------
doc     | Upon a slitted sheet I sit.
doc_tsv | 'sheet':4 'sit':6 'slit':3 'upon':1
-[ RECORD 5 ]----------------------------------------------------
doc     | Whoever slit the sheets is a good sheet slitter.
doc_tsv | 'good':7 'sheet':4,8 'slit':2 'slitter':9 'whoever':1
-[ RECORD 6 ]----------------------------------------------------
doc     | I am a sheet slitter.
doc_tsv | 'sheet':4 'slitter':5
-[ RECORD 7 ]----------------------------------------------------
doc     | I slit sheets.
doc_tsv | 'sheet':3 'slit':2
-[ RECORD 8 ]----------------------------------------------------
doc     | I am the sleekest sheet slitter that ever slit sheets.
doc_tsv | 'ever':8 'sheet':5,10 'sleekest':4 'slit':9 'slitter':6
-[ RECORD 9 ]----------------------------------------------------
doc     | She slits the sheet she sits on.
doc_tsv | 'sheet':4 'sit':6 'slit':2

那么索引是如何组织的呢?这里不能直接使用R-tree,因为这里无法为文档定义矩形边界。但是我们可以做一些修改,将集合理解为词素的集合。
RD树的想法是用边界集(即包含子集的所有元素的集)替换边界矩形
这里一个重要的问题是提出了如何在索引行中表示集合。最直接的方法就是枚举集合中的所有元素。如下所示:
在这里插入图片描述
然后,如果通过条件doc_tsv @@ to_tsquery(‘sit’)进行访问,我们将下降到包含«sit»的那些节点:

在这里插入图片描述
这种表示方法存在明显的问题。文档中的词素数量可能非常大,因此索引行将比较大的并进入TOAST,从而使索引的效率大大降低。即使每个文档词素很少,集合的并集仍然可能非常大:根目录越高,索引行就越大。

有时候会使用以上的组织形式,但都是用于其他数据类型。全文搜索使用另一个更紧凑的解决方案-签名树。和bloom过滤的方法类似。
每个词素都可以用其签名表示:特定长度的bit位串,其中除一位以外的所有位均为零。该bit位的位置由计算词素的哈希函数的值确定。
文档签名是文档中词素bit位按位或操作。
假设我们有以下词素的签名:

could    1000000
ever     0001000
good     0000010
mani     0000100
sheet    0000100
sleekest 0100000
sit      0010000
slit     0001000
slitter  0000001
upon     0000010
whoever  0010000

那么,文档签名如下:

Can a sheet slitter slit sheets?                       0001101
How many sheets could a sheet slitter slit?            1001101
I slit a sheet, a sheet I slit.                        0001100
Upon a slitted sheet I sit.                            0011110
Whoever slit the sheets is a good sheet slitter.       0011111
I am a sheet slitter.                                  0000101
I slit sheets.                                         0001100
I am the sleekest sheet slitter that ever slit sheets. 0101101
She slits the sheet she sits on.                       0011100

索引树的组织结构如下:
在这里插入图片描述

这种方法的优点显而易见:索引行的大小相等,并且这样的索引紧凑。但缺点也很明显:为了紧凑牺牲了精确性。
让我们考虑相同搜索条件doc_tsv @@ to_tsquery(‘sit’)。让我们以与文档相同的方式计算搜索查询的签名:sit的签名为0010000。一致性函数必须返回包含至少符合一个bit位签名的所有子节点:这里第三个bit位是1,所以要返回所有包含第三个bit
位是1的子节点。
在这里插入图片描述

上图中:我们可以看到树中有的节点变成了黄色,这意味着出现误报,并且在搜索过程中经过了过多的节点。在这里,我们看到“ whoever”语素,其签名与“ sit”相同。重要的是,这种方式不会出现误报,我们不会错过所需的值。

此外,不同的文档也可能获得相同的签名:在以上的示例中,«I slit a sheet, a sheet I slit»和«I slit sheets»(都具有0001100的签名)。而且,如果叶索引行未存储«tsvector»的值,则索引本身将会误报。当然,在这种情况下,这种方法要求索引引擎重新检查表的结果,所以我们不会看到这些误报。但是搜索效率会受到影响。

实际上,签名是124字节,而不是图中的7位,因此在真实环境中,上述问题发生的可能性要小得多。而且真实环境中,有更多文档被索引。为了减少误报量,在实现上还是有些小麻烦:被索引的“ tsvector”存储在叶索引行中,但前提是不能太大(小于索引页面的1/16)。如果page是8KB,那么大约为0.5KB)。

实例:

为了查看索引对实际数据的工作方式,https://oc.postgrespro.ru/index.php/s/fRxTZ0sVfPZzbmd有相关电子邮件的存档。该示例中包含356125条消息,其中包含发送日期,主题,作者和文本。

ts=# select * from mail_messages order by sent limit 1;
-[ RECORD 1 ]------------------------------------------------------------------------
id         | 1572389
parent_id  | 1562808
sent       | 1997-06-24 11:31:09
subject    | Re: [HACKERS] Array bug is still there....
author     | "Thomas G. Lockhart" <Thomas.Lockhart@jpl.nasa.gov>
body_plain | Andrew Martin wrote:                                                    +
           | > Just run the regression tests on 6.1 and as I suspected the array bug +
           | > is still there. The regression test passes because the expected output+
           | > has been fixed to the *wrong* output.                                 +
           |                                                                         +
           | OK, I think I understand the current array behavior, which is apparently+
           | different than the behavior for v1.0x.                                  +
             ...

构建“ tsvector”类型的列,并创建索引。在这里,我们将三个值合并在一个tsvector类型的列中(主题,作者和消息文本),以表明文档不必是一个字段,而是可以包含完全不同的任意部分。

fts=# alter table mail_messages add column tsv tsvector;

fts=# update mail_messages
set tsv = to_tsvector(subject||' '||author||' '||body_plain);
NOTICE:  word is too long to be indexed
DETAIL:  Words longer than 2047 characters are ignored.
...
UPDATE 356125
fts=# create index on mail_messages using gist(tsv); 

如我们所见,由于太大而丢弃了一定数量的单词。但是该索引还是可以成功创建,并且支持搜索查询:

fts=# explain (analyze, costs off)
select * from mail_messages where tsv @@ to_tsquery('magic & value');
                        QUERY PLAN
----------------------------------------------------------
 Index Scan using mail_messages_tsv_idx on mail_messages
 (actual time=0.998..416.335 rows=898 loops=1)
   Index Cond: (tsv @@ to_tsquery('magic & value'::text))
   Rows Removed by Index Recheck: 7859
 Planning time: 0.203 ms
 Execution time: 416.492 ms
(5 rows)

可以看到,与符合条件行是898行,另外通过重新检查表而被过滤掉了7859行。这证明了较低的准确性影响查询的效率。

内部结构

通过gevel插件查看索引的信息:

fts=# select level, a
from gist_print('mail_messages_tsv_idx') as t(level int, valid bool, a gtsvector)
where a is not null;
 level |               a              
-------+-------------------------------
     1 | 992 true bits, 0 false bits
     2 | 988 true bits, 4 false bits
     3 | 573 true bits, 419 false bits
     4 | 65 unique words
     4 | 107 unique words
     4 | 64 unique words
     4 | 42 unique words
...

存储在索引行的类型“ gtsvector”的值实际上是签名的bit位的值加上<tsvector源的值。如果向量可用,输出包含词素(唯一单词)的数目,否则,输出签名中的false和true bit位的数目。
可以看到,在根节点中,签名全是1。所以level 1是无效的,level 2只有4个false bits位,几乎也是无效的。

Gist索引相关的属性

Gsit支持的访问方法:

 amname |     name      | pg_indexam_has_property
--------+---------------+-------------------------
 gist   | can_order     | f
 gist   | can_unique    | f
 gist   | can_multi_col | t
 gist   | can_exclude   | t

不支持值的排序和唯一约束。但是索引可以建立在多个列上,并用于排除约束中。

索引层属性:

     name      | pg_index_has_property
---------------+-----------------------
 clusterable   | t
 index_scan    | t
 bitmap_scan   | t
 backward_scan | f

最有趣的是列层的属性。一些属性独立于运算符类:

        name        | pg_index_column_has_property
--------------------+------------------------------
 asc                | f
 desc               | f
 nulls_first        | f
 nulls_last         | f
 orderable          | f
 search_array       | f
 search_nulls       | t

不支持排序,不支持数组搜索,支持NULL值搜索。
但是剩下的两个属性«distance_orderable»和«returnable»将取决于所使用的运算符类。例如,点运算符:

        name        | pg_index_column_has_property
--------------------+------------------------------
 distance_orderable | t
 returnable         | t

第一个属性表明距离运算符可用于搜索最近的邻居。第二个告诉索引可以用于仅索引扫描。尽管叶索引行存储矩形范围而不是点,但是访问方法可以返回所需的内容。

间隔运算符类:

--------------------+------------------------------
 distance_orderable | f
 returnable         | t

由于没有定义举例函数,所以无法计算最近相邻的值。

全文搜索:

        name        | pg_index_column_has_property
--------------------+------------------------------
 distance_orderable | f
 returnable         | f

不支持仅索引扫描,因为索引的叶子节点行只能包含签名,而不能包含数据本身。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值