暂无图片
暂无图片
6
暂无图片
暂无图片
暂无图片

PG 索引相关系统表

原创 吴松 云和恩墨 2021-09-30
1788

接口

前一篇提到,要使用索引,必须获得索引相关的一些信息。PG 中索引相关的信息存储在 pg_am 中:

postgres=# select amname from pg_am;
    
 amname
--------
 btree
 hash
 gist
 gin
 spgist
 brin
(6 rows)
复制

顺序扫描也可以视作一种 Access Method,由于历史原因,它不在 pg_am 的记录中。
在 PostgreSQL 9.5 及更低版本中,每个属性都用“pg_am”表的一个单独字段表示。 从 9.6 版本开始,使用特殊函数查询属性并分为几层:

访问方法属性 - “pg_indexam_has_property”
特定索引的属性 - “pg_index_has_property”
索引的各个列的属性 - “pg_index_column_has_property”
访问方法层和索引层分开,着眼于未来:截至目前,基于一种访问方法的所有索引将始终具有相同的属性。

可以通过官方文档查询 pg_am 表结构的一些变化。

Operator classes and families

除了接口 pg_am 中定义的索引相关属性外,还需要了解索引能够支持哪些数据类型运算符,PG 为此引入了 operator class 和 operator family 的概念。

一个 operator class 包含一组 operators 的最小集合(可能还有辅助函数),用于支持索引处理特定的数据类型。

一个 operator class 被包含在某个 operator family 中。此外,一个通用的 operator family 中可能包含多个 operator class,如果这些 operator class 具有相同的语义。
例如: “integer_ops” 这个 operator family 包含 “int8_ops”、“int4_ops”、“int2_ops” 这几个 operator class,分别支持 “bigint”、“integer”、“smallint” 这三种数据类型。这三种数据类型虽然不同,但是有相同的语义,都是整数类型。

postgres=# select opfname, opcname, opcintype::regtype
from pg_opclass opc, pg_opfamily opf
where opf.opfname = 'integer_ops'
and opc.opcfamily = opf.oid
and opf.opfmethod = (
      select oid from pg_am where amname = 'btree'
    );

   opfname   | opcname  | opcintype
-------------+----------+-----------
 integer_ops | int2_ops | smallint
 integer_ops | int4_ops | integer
 integer_ops | int8_ops | bigint
(3 rows)

复制

一个 operator family 还可以包含额外的 operators 用于比较类型不同的值的大小。按照 family 进行分组,使得执行计划能够对不同类型的值的谓词使用索引。

在大多数情况下,我们不需要了解有关 operator class 和 operator family 的任何信息。 通常我们只是创建一个索引,默认使用某个操作符类。

我们也可以明确指定 operator class。 这是一个何时需要显式规范的简单示例:在排序规则与 C 不同的数据库中,常规索引不支持 LIKE 操作,可以通过显示指定 operator class 的方式创建索引,来克服这个限制:

postgres=# explain (costs off) select * from t where b like 'A%';

         QUERY PLAN          
-----------------------------
 Seq Scan on t
   Filter: (b ~~ 'A%'::text)
(2 rows)

postgres=# create index on t(b text_pattern_ops);

postgres=# explain (costs off) select * from t where b like 'A%';

                           QUERY PLAN                          
----------------------------------------------------------------
 Bitmap Heap Scan on t
   Filter: (b ~~ 'A%'::text)
   ->  Bitmap Index Scan on t_b_idx1
         Index Cond: ((b ~>=~ 'A'::text) AND (b ~<~ 'B'::text))
(4 rows)

复制

System catalog

系统表中与 operator class 和 operator family 直接相关联的一些表的关联图,如下:
image.png

pg_am 中存储了系统支持的索引类型,及索引相关属性。
pg_opfamily
存储了 opfamily 和支持的索引的信息。每个 operator family 都是 opeartors 和相关支持例程的集合,这些例程为特定索引类型实现了指定的语义。
在同一 family 下的 operators 是“兼容的”,兼容的方式有访问方法决定。Operator family 的概念允许跨数据类型的运算符与索引一起使用,并使用索引的语义进行推理。
image.png

pg_amop
pg_amop 存储有关与索引 operator family 相关的操作符的信息。 operator family 中的每一个 operator 在 pg_amop中都有一行。operator 可以是搜索运算符或排序运算符。 一个 operator 可以出现在多个 family 中,但不能出现在多个搜索位置,也不能出现在一个 family 中的多个排序位置。
image.png

pg_amproc
pg_amproc 中存储了索引对应的 opeartor familes 相关的 support procedures 的信息。每个 operator family 的每个 support procedure 有一行记录。

image.png

pg_opclass
pg_opclass 定义了索引访问方法的 operator class。 每个 operator class 为特定数据类型的索引列和特定索引访问方法定义语义。 operator class 本质上指定特定的 operator family 适用于特定的可索引列数据类型。该 family 中实际可用于索引列的运算符集是接受列的数据类型作为其左侧输入的运算符。
image.png
可以通过查询 pg_opclass 找到每一个索引类型支持的操 operator class,以及对应的索引的输入 和输出 (key, value)

postgres=# select opcname, opcintype::regtype
from pg_opclass
where opcmethod = (select oid from pg_am where amname = 'btree')
order by opcintype::regtype::text;

       opcname       |          opcintype          
---------------------+-----------------------------
 abstime_ops         | abstime
 array_ops           | anyarray
 enum_ops            | anyenum
...

复制

可以从 pg_amop 中查找,索引支持的数据类型及对应的谓词:

postgres=# select amop.amopopr::regoperator
from pg_opclass opc, pg_opfamily opf, pg_am am, pg_amop amop
where opc.opcname = 'array_ops'
and opf.oid = opc.opcfamily
and am.oid = opf.opfmethod
and amop.amopfamily = opc.opcfamily
and am.amname = 'btree'
and amop.amoplefttype = opc.opcintype;

        amopopr        
-----------------------
 <(anyarray,anyarray)
 <=(anyarray,anyarray)
 =(anyarray,anyarray)
 >=(anyarray,anyarray)
 >(anyarray,anyarray)

复制

举例说明:

例如我们现在想要使用 gist 索引,首先我们要查看 pg_am 中支持的索引类型,确定当前系统是否支持 gist 索引。

postgres=# select oid,amname from pg_am;
 oid  | amname 
------+--------
  403 | btree
  405 | hash
  783 | gist
 2742 | gin
 4000 | spgist
 4039 | psort
 4239 | cbtree
 4444 | cgin
(8 rows)

复制

可以看到 pg_am 中有 gist 索引的类型,然后通过 pg_opclass 查看一下 gist 索引支持哪些类型。

postgres=# select * from pg_opclass where opcmethod = 783;
 opcmethod |   opcname    | opcnamespace | opcowner | opcfamily | opcintype | opcdefault | opckeytype 
-----------+--------------+--------------+----------+-----------+-----------+------------+------------
       783 | box_ops      |           11 |       10 |      2593 |       603 | t          |          0
       783 | point_ops    |           11 |       10 |      1029 |       600 | t          |        603
       783 | poly_ops     |           11 |       10 |      2594 |       604 | t          |        603
       783 | circle_ops   |           11 |       10 |      2595 |       718 | t          |        603
       783 | tsvector_ops |           11 |       10 |      3655 |      3614 | t          |       3642
       783 | tsquery_ops  |           11 |       10 |      3702 |      3615 | t          |         20
       783 | range_ops    |           11 |       10 |      3919 |      3831 | t          |          0
(7 rows)

复制

以 box_ops 为例,其索引的数据类型为 603 ,索引中 key 的数据类型为 603 ,查询 pg_type 获得数据类型。

postgres=# select * from pg_type where oid = 603;
 typname | typnamespace | typowner | typlen | typbyval | typtype | typcategory | typispreferred | typisdefi
ned | typdelim | typrelid | typelem | typarray | typinput | typoutput | typreceive | typsend  | typmodin | 
typmodout | typanalyze | typalign | typstorage | typnotnull | typbasetype | typtypmod | typndims | typcolla
tion | typdefaultbin | typdefault | typacl 
---------+--------------+----------+--------+----------+---------+-------------+----------------+----------
----+----------+----------+---------+----------+----------+-----------+------------+----------+----------+-
----------+------------+----------+------------+------------+-------------+-----------+----------+---------
-----+---------------+------------+--------
 box     |           11 |       10 |     32 | f        | b       | G           | f              | t        
    | ;        |        0 |     600 |     1020 | box_in   | box_out   | box_recv   | box_send | -        | 
-         | -          | d        | p          | f          |           0 |        -1 |        0 |         
   0 |               |            | 
(1 row)

复制

查询 pg_amproc 获取索引关联的 opfamily 的支持例程

postgres=# select * from pg_amproc where amprocfamily = 2593;
 amprocfamily | amproclefttype | amprocrighttype | amprocnum |       amproc        
--------------+----------------+-----------------+-----------+---------------------
         2593 |            603 |             603 |         1 | gist_box_consistent
         2593 |            603 |             603 |         2 | gist_box_union
         2593 |            603 |             603 |         3 | gist_box_compress
         2593 |            603 |             603 |         4 | gist_box_decompress
         2593 |            603 |             603 |         5 | gist_box_penalty
         2593 |            603 |             603 |         6 | gist_box_picksplit
         2593 |            603 |             603 |         7 | gist_box_same
(7 rows)

复制

这些例程是支持索引的增删改查需要的处理函数,在构建索引以及使用索引的过程中需要用到这些例程。例如,支持 box 类型的数据 gist 索引需要用的例程为 gist_box_consistent、gist_box_union … ; 支持 point 类型的数据 gist 索引需要用的的例程为 gist_point_consistent … 。 具体的例程的信息存储在 pg_proc 中。

pg_amop 中定义了索引相关的操作符信息,通常这对应 SQL 语句中的谓词部分的内容。

select * from pg_amop where amopfamily  = 2593;
amopfamily | amoplefttype | amoprighttype | amopstrategy | amoppurpose | amopopr | amopmethod | amopsortfa
mily 
------------+--------------+---------------+--------------+-------------+---------+------------+-----------
-----
       2593 |          603 |           603 |            1 | s           |     493 |        783 |           
   0
       2593 |          603 |           603 |            2 | s           |     494 |        783 |           
   0
       2593 |          603 |           603 |            3 | s           |     500 |        783 |           
   0
       2593 |          603 |           603 |            4 | s           |     495 |        783 |           
   0
       2593 |          603 |           603 |            5 | s           |     496 |        783 |           
   0
       2593 |          603 |           603 |            6 | s           |     499 |        783 |           
   0
       2593 |          603 |           603 |            7 | s           |     498 |        783 |           
   0
       2593 |          603 |           603 |            8 | s           |     497 |        783 |           
   0
       2593 |          603 |           603 |            9 | s           |    2571 |        783 |           
   0
       2593 |          603 |           603 |           10 | s           |    2570 |        783 |           
   0

复制

例如:
2593 | 603 | 603 | 1 | s | 493 | 783 | 0 |
这样一行数据定义类查询语句中谓词部分操作符左右两侧的数据类型 603 ,操作符的类型 493 ,操作符的目的 s (s 搜索, o 表示排序),查询 pg_operator 获取具体的操作符类型 。

postgres=# select * from pg_operator where oid = 493;
-[ RECORD 1 ]+----------------
oprname      | <<
oprnamespace | 11
oprowner     | 10
oprkind      | b
oprcanmerge  | f
oprcanhash   | f
oprleft      | 603
oprright     | 603
oprresult    | 16
oprcom       | 0
oprnegate    | 0
oprcode      | box_left
oprrest      | positionsel
oprjoin      | positionjoinsel

复制

对应的操作符为 " << " ,操作符左右两侧数据类型为 603,操作符的结果类型为 16 ,实现该操作符的函数为 box_left 。

通过以上相关的系统表,可以获取系统中支持哪些索引类型,每种索引类型支持哪些数据类型和操作符,以及操作符对应的处理函数,索引使用中需要用到的各种例程等信息。

参考文档:https://postgrespro.com/blog/pgsql/4161264

「喜欢这篇文章,您的关注和赞赏是给作者最好的鼓励」
关注作者
【版权声明】本文为墨天轮用户原创内容,转载时必须标注文章的来源(墨天轮),文章链接,文章作者等基本信息,否则作者和墨天轮有权追究责任。如果您发现墨天轮中有涉嫌抄袭或者侵权的内容,欢迎发送邮件至:contact@modb.pro进行举报,并提供相关证据,一经查实,墨天轮将立刻删除相关内容。

文章被以下合辑收录

评论