引言
PostgreSQL 中,访问方法(Access Method,AM)表示用于访问数据库表中数据的一类算法。为了方便在系统中添加新的访问方法,PostgreSQL 定义了一套通用的AM接口,它们的主要作用读取表数据并按照定义的规则处理。PostgreSQL 支持的索引方法就是一类 AM。以 PostgreSQL 11.9 为例(后续没有特别说明,举例版本均为 11.9),系统内置了 6 种索引方法(B-Tree,Hash,GIST,SP-GIST,GIN和BGIN),其它索引方法可以通过扩展或者 SQL 指令进行定义。
索引访问方法系统表
索引方法必须提供必要的信息才能够被系统正常使用,这些信息记录在相关的系统表中。记录索引方法的相关系统表包括 pg_am、pg_opfamily、pg_opclass、pg_amop、pg_amproc。接下来将分别介绍这些系统表的作用,有关每个系统表每个列的语意可以参考 PostgreSQL 官方文档,这里就不再赘述。
pg_am 系统表
pg_am中记录了支持的AM。系统支持的每种AM在pg_am中都有一行记录。在 PostgreSQL 9.6 之前,pg_am中包含了很多额外的列表示索引访问方法的性质。而从 9.6 开始,这些性质被放到更深层次,系统增加了 pg_index_column_has_property()和一些相关的函数来支持查看、检查索引访问方法的性质。
系统默认pg_am查询结果示例:
postgres=# select oid, * from pg_am;
oid | amname | amhandler | amtype
------+--------+-------------+--------
403 | btree | bthandler | i
405 | hash | hashhandler | i
783 | gist | gisthandler | i
2742 | gin | ginhandler | i
4000 | spgist | spghandler | i
3580 | brin | brinhandler | i
(6 rows)
pg_opclass 系统表
pg_opclass中记录了索引访问方法的“操作符类(operator class)”。一种索引访问方法可以用于不同的数据类型,它并不直接管理目标字段的类型,这些信息由 pg_class 系统表来维护。一个“操作符类”定义了一种具体类型用于一种索引访问方法的语义。索引方法中用策略(strategy)号标识需要的操作符,用支持函数(support function)号标识需要的函数。
以 Btree 索引为例,Btree 需要一种类型提供 5 种策略以及3种支持函数(1种必选,2种可选),才能正确适配这种类型。假设定义了int4 类型,需要适配 Btree 索引,则需要创建一个 int4 类型的操作符类,里面的成员至少包括 < / <= / = / >= / > 和 btint4cmp(int4, int4) 函数。
这里需要强调的是 1)一个“操作符类”只针对一种类型,不能出现跨类型的策略或者支持函数,这也是与下面讲解的“操作符族(opfamily)”最主要不同点之一;2)“操作符类”包括下面讲解的“操作符族”中需要的成员只是“占位”的作用,真正的定义工作需要用定义操作符和定义函数的指令去完成;3)pg_opcalss 可以为同一种索引方法的同一种类型定义多个“操作符类”,但是默认的“操作符类”只能有一个。
Btree 支持的“操作符类”的查询结果示例:
postgres=# select *, opcintype::regtype as opcintypename from pg_opclass where opcmethod = 403;
opcmethod | opcname | opcnamespace | opcowner | opcfamily | opcintype | opcdefault | opckeytype | opcintypename
-----------+---------------------+--------------+----------+-----------+-----------+------------+------------+-----------------------------
403 | abstime_ops | 11 | 10 | 421 | 702 | t | 0 | abstime
403 | array_ops | 11 | 10 | 397 | 2277 | t | 0 | anyarray
403 | bit_ops | 11 | 10 | 423 | 1560 | t | 0 | bit
403 | bool_ops | 11 | 10 | 424 | 16 | t | 0 | boolean
403 | bpchar_ops | 11 | 10 | 426 | 1042 | t | 0 | character
403 | bytea_ops | 11 | 10 | 428 | 17 | t | 0 | bytea
403 | char_ops | 11 | 10 | 429 | 18 | t | 0 | "char"
403 | cidr_ops | 11 | 10 | 1974 | 869 | f | 0 | inet
403 | date_ops | 11 | 10 | 434 | 1082 | t | 0 | date
403 | float4_ops | 11 | 10 | 1970 | 700 | t | 0 | real
403 | float8_ops | 11 | 10 | 1970 | 701 | t | 0 | double precision
403 | inet_ops | 11 | 10 | 1974 | 869 | t | 0 | inet
403 | int2_ops | 11 | 10 | 1976 | 21 | t | 0 | smallint
403 | int4_ops | 11 | 10 | 1976 | 23 | t | 0 | integer
403 | int8_ops | 11 | 10 | 1976 | 20 | t | 0 | bigint
403 | interval_ops | 11 | 10 | 1982 | 1186 | t | 0 | interval
403 | macaddr_ops | 11 | 10 | 1984 | 829 | t | 0 | macaddr
403 | macaddr8_ops | 11 | 10 | 3371 | 774 | t | 0 | macaddr8
403 | name_ops | 11 | 10 | 1986 | 19 | t | 2275 | name
403 | numeric_ops | 11 | 10 | 1988 | 1700 | t | 0 | numeric
403 | oid_ops | 11 | 10 | 1989 | 26 | t | 0 | oid
403 | oidvector_ops | 11 | 10 | 1991 | 30 | t | 0 | oidvector
403 | record_ops | 11 | 10 | 2994 | 2249 | t | 0 | record
403 | record_image_ops | 11 | 10 | 3194 | 2249 | f | 0 | record
403 | text_ops | 11 | 10 | 1994 | 25 | t | 0 | text
403 | time_ops | 11 | 10 | 1996 | 1083 | t | 0 | time without time zone
403 | timestamptz_ops | 11 | 10 | 434 | 1184 | t | 0 | timestamp with time zone
403 | timetz_ops | 11 | 10 | 2000 | 1266 | t | 0 | time with time zone
403 | varbit_ops | 11 | 10 | 2002 | 1562 | t | 0 | bit varying
403 | varchar_ops | 11 | 10 | 1994 | 25 | f | 0 | text
403 | timestamp_ops | 11 | 10 | 434 | 1114 | t | 0 | timestamp without time zone
403 | text_pattern_ops | 11 | 10 | 2095 | 25 | f | 0 | text
403 | varchar_pattern_ops | 11 | 10 | 2095 | 25 | f | 0 | text
403 | bpchar_pattern_ops | 11 | 10 | 2097 | 1042 | f | 0 | character
403 | money_ops | 11 | 10 | 2099 | 790 | t | 0 | money
403 | tid_ops | 11 | 10 | 2789 | 27 | t | 0 | tid
403 | reltime_ops | 11 | 10 | 2233 | 703 | t | 0 | reltime
403 | tinterval_ops | 11 | 10 | 2234 | 704 | t | 0 | tinterval
403 | uuid_ops | 11 | 10 | 2968 | 2950 | t | 0 | uuid
403 | pg_lsn_ops | 11 | 10 | 3253 | 3220 | t | 0 | pg_lsn
403 | enum_ops | 11 | 10 | 3522 | 3500 | t | 0 | anyenum
403 | tsvector_ops | 11 | 10 | 3626 | 3614 | t | 0 | tsvector
403 | tsquery_ops | 11 | 10 | 3683 | 3615 | t | 0 | tsquery
403 | range_ops | 11 | 10 | 3901 | 3831 | t | 0 | anyrange
403 | jsonb_ops | 11 | 10 | 4033 | 3802 | t | 0 | jsonb
(45 rows)
pg_opfamily 系统表
pg_opfamily中记录了索引访问方法的“操作符族(operator family)”。虽然一个特定的索引列中必然只有一种数据类型,但是被索引列与另一种不同类型的值进行比较的场景也很普遍。如果能建立起不同类型之间联系,使得上述不同类型比较的场景也能够使用索引,优化器就能在更多场景下利用索引访问方法进行执行计划规划。不同类型之间建立联系需要满足一定的兼容规则,但这超出了本文介绍的范围。
为了满足这些需求,PostgreSQL 引入了“操作符族”的概念。一个“操作符族”包含一个或者多个“操作符类”,并且也包含不属于任何一个“操作符类”的策略或支持函数。这些不属于任何一个“操作符类”成员的作用就是满足跨类型比较使用索引的需求。
以系统内置一个的 Btree “操作符族” integer_ops 为例,它包括了分别用于 int8、int4、int2 类型的“操作符类”,同时也包括了三种类型跨类型比较的操作符和函数,这允许对这些类型中的任意两种进行比较,都能够使用定义的索引。
这里需要强调是 1)任何一个“操作符类”必须属于某一个“操作符族”,如果定义时没有指定属于的“操作符族”,则系统会默认创建一个与其同名的“操作符族”;2)系统表中记录的操族符和函数成员与索引访问方法之间的映射关系实际上是以“操作符族”分组的,与“操作符类”没有什么关系;3)PostgreSQL 引入“操作符类”是为了强调某个特定索引对“操作符族”的依赖程度;实际使用中“操作符类”的信息会在创建索引时被记录,然后再判断条件是否能使用索引时,根据“操作符类”找到相应的“操作符族”,在找到其中包含的成员,做进一步判断。
Btree 支持的“操作符族”的查询结果示例:
postgres=# select oid, * from pg_opfamily where opfmethod =403;
oid | opfmethod | opfname | opfnamespace | opfowner
------+-----------+--------------------+--------------+----------
421 | 403 | abstime_ops | 11 | 10
397 | 403 | array_ops | 11 | 10
423 | 403 | bit_ops | 11 | 10
424 | 403 | bool_ops | 11 | 10
426 | 403 | bpchar_ops | 11 | 10
428 | 403 | bytea_ops | 11 | 10
429 | 403 | char_ops | 11 | 10
434 | 403 | datetime_ops | 11 | 10
1970 | 403 | float_ops | 11 | 10
1974 | 403 | network_ops | 11 | 10
1976 | 403 | integer_ops | 11 | 10
1982 | 403 | interval_ops | 11 | 10
1984 | 403 | macaddr_ops | 11 | 10
3371 | 403 | macaddr8_ops | 11 | 10
1986 | 403 | name_ops | 11 | 10
1988 | 403 | numeric_ops | 11 | 10
1989 | 403 | oid_ops | 11 | 10
1991 | 403 | oidvector_ops | 11 | 10
2994 | 403 | record_ops | 11 | 10
3194 | 403 | record_image_ops | 11 | 10
1994 | 403 | text_ops | 11 | 10
1996 | 403 | time_ops | 11 | 10
2000 | 403 | timetz_ops | 11 | 10
2002 | 403 | varbit_ops | 11 | 10
2095 | 403 | text_pattern_ops | 11 | 10
2097 | 403 | bpchar_pattern_ops | 11 | 10
2099 | 403 | money_ops | 11 | 10
2789 | 403 | tid_ops | 11 | 10
2233 | 403 | reltime_ops | 11 | 10
2234 | 403 | tinterval_ops | 11 | 10
2968 | 403 | uuid_ops | 11 | 10
3253 | 403 | pg_lsn_ops | 11 | 10
3522 | 403 | enum_ops | 11 | 10
3626 | 403 | tsvector_ops | 11 | 10
3683 | 403 | tsquery_ops | 11 | 10
3901 | 403 | range_ops | 11 | 10
4033 | 403 | jsonb_ops | 11 | 10
(37 rows)
pg_amop 系统表
pg_amop中记录索引访问方法“操作符族”中的操作符信息。一个操作符可以出现在多个“操作符族”中,但是在同一个“操作符族”中不允许出现多次。
Btree 支持的 integer_ops “操作符族”的操作符查询结果示例:
postgres=# select *, amoplefttype::regtype as lefttypename, amoprighttype::regtype as righttypename from pg_amop where amopmethod = 403 and amopfamily = 1976;
amopfamily | amoplefttype | amoprighttype | amopstrategy | amoppurpose | amopopr | amopmethod | amopsortfamily | lefttypename | righttypename
------------+--------------+---------------+--------------+-------------+---------+------------+----------------+--------------+---------------
1976 | 21 | 21 | 1 | s | 95 | 403 | 0 | smallint | smallint
1976 | 21 | 21 | 2 | s | 522 | 403 | 0 | smallint | smallint
1976 | 21 | 21 | 3 | s | 94 | 403 | 0 | smallint | smallint
1976 | 21 | 21 | 4 | s | 524 | 403 | 0 | smallint | smallint
1976 | 21 | 21 | 5 | s | 520 | 403 | 0 | smallint | smallint
1976 | 21 | 23 | 1 | s | 534 | 403 | 0 | smallint | integer
1976 | 21 | 23 | 2 | s | 540 | 403 | 0 | smallint | integer
1976 | 21 | 23 | 3 | s | 532 | 403 | 0 | smallint | integer
1976 | 21 | 23 | 4 | s | 542 | 403 | 0 | smallint | integer
1976 | 21 | 23 | 5 | s | 536 | 403 | 0 | smallint | integer
1976 | 21 | 20 | 1 | s | 1864 | 403 | 0 | smallint | bigint
1976 | 21 | 20 | 2 | s | 1866 | 403 | 0 | smallint | bigint
1976 | 21 | 20 | 3 | s | 1862 | 403 | 0 | smallint | bigint
1976 | 21 | 20 | 4 | s | 1867 | 403 | 0 | smallint | bigint
1976 | 21 | 20 | 5 | s | 1865 | 403 | 0 | smallint | bigint
1976 | 23 | 23 | 1 | s | 97 | 403 | 0 | integer | integer
1976 | 23 | 23 | 2 | s | 523 | 403 | 0 | integer | integer
1976 | 23 | 23 | 3 | s | 96 | 403 | 0 | integer | integer
1976 | 23 | 23 | 4 | s | 525 | 403 | 0 | integer | integer
1976 | 23 | 23 | 5 | s | 521 | 403 | 0 | integer | integer
1976 | 23 | 21 | 1 | s | 535 | 403 | 0 | integer | smallint
1976 | 23 | 21 | 2 | s | 541 | 403 | 0 | integer | smallint
1976 | 23 | 21 | 3 | s | 533 | 403 | 0 | integer | smallint
1976 | 23 | 21 | 4 | s | 543 | 403 | 0 | integer | smallint
1976 | 23 | 21 | 5 | s | 537 | 403 | 0 | integer | smallint
1976 | 23 | 20 | 1 | s | 37 | 403 | 0 | integer | bigint
1976 | 23 | 20 | 2 | s | 80 | 403 | 0 | integer | bigint
1976 | 23 | 20 | 3 | s | 15 | 403 | 0 | integer | bigint
1976 | 23 | 20 | 4 | s | 82 | 403 | 0 | integer | bigint
1976 | 23 | 20 | 5 | s | 76 | 403 | 0 | integer | bigint
1976 | 20 | 20 | 1 | s | 412 | 403 | 0 | bigint | bigint
1976 | 20 | 20 | 2 | s | 414 | 403 | 0 | bigint | bigint
1976 | 20 | 20 | 3 | s | 410 | 403 | 0 | bigint | bigint
1976 | 20 | 20 | 4 | s | 415 | 403 | 0 | bigint | bigint
1976 | 20 | 20 | 5 | s | 413 | 403 | 0 | bigint | bigint
1976 | 20 | 21 | 1 | s | 1870 | 403 | 0 | bigint | smallint
1976 | 20 | 21 | 2 | s | 1872 | 403 | 0 | bigint | smallint
1976 | 20 | 21 | 3 | s | 1868 | 403 | 0 | bigint | smallint
1976 | 20 | 21 | 4 | s | 1873 | 403 | 0 | bigint | smallint
1976 | 20 | 21 | 5 | s | 1871 | 403 | 0 | bigint | smallint
1976 | 20 | 23 | 1 | s | 418 | 403 | 0 | bigint | integer
1976 | 20 | 23 | 2 | s | 420 | 403 | 0 | bigint | integer
1976 | 20 | 23 | 3 | s | 416 | 403 | 0 | bigint | integer
1976 | 20 | 23 | 4 | s | 430 | 403 | 0 | bigint | integer
1976 | 20 | 23 | 5 | s | 419 | 403 | 0 | bigint | integer
(45 rows)pg_amproc 系统表
pg_amproc中记录索引访问方法“操作符族”中的支持函数信息。
Btree 支持的 integer_ops “操作符族”的支持函数查询结果示例:
postgres=# select *, amoplefttype::regtype as lefttypename, amoprighttype::regtype as righttypename from pg_amop where amopmethod = 403 and amopfamily = 1976;
amopfamily | amoplefttype | amoprighttype | amopstrategy | amoppurpose | amopopr | amopmethod | amopsortfamily | lefttypename | righttypename
------------+--------------+---------------+--------------+-------------+---------+------------+----------------+--------------+---------------
1976 | 21 | 21 | 1 | s | 95 | 403 | 0 | smallint | smallint
1976 | 21 | 21 | 2 | s | 522 | 403 | 0 | smallint | smallint
1976 | 21 | 21 | 3 | s | 94 | 403 | 0 | smallint | smallint
1976 | 21 | 21 | 4 | s | 524 | 403 | 0 | smallint | smallint
1976 | 21 | 21 | 5 | s | 520 | 403 | 0 | smallint | smallint
1976 | 21 | 23 | 1 | s | 534 | 403 | 0 | smallint | integer
1976 | 21 | 23 | 2 | s | 540 | 403 | 0 | smallint | integer
1976 | 21 | 23 | 3 | s | 532 | 403 | 0 | smallint | integer
1976 | 21 | 23 | 4 | s | 542 | 403 | 0 | smallint | integer
1976 | 21 | 23 | 5 | s | 536 | 403 | 0 | smallint | integer
1976 | 21 | 20 | 1 | s | 1864 | 403 | 0 | smallint | bigint
1976 | 21 | 20 | 2 | s | 1866 | 403 | 0 | smallint | bigint
1976 | 21 | 20 | 3 | s | 1862 | 403 | 0 | smallint | bigint
1976 | 21 | 20 | 4 | s | 1867 | 403 | 0 | smallint | bigint
1976 | 21 | 20 | 5 | s | 1865 | 403 | 0 | smallint | bigint
1976 | 23 | 23 | 1 | s | 97 | 403 | 0 | integer | integer
1976 | 23 | 23 | 2 | s | 523 | 403 | 0 | integer | integer
1976 | 23 | 23 | 3 | s | 96 | 403 | 0 | integer | integer
1976 | 23 | 23 | 4 | s | 525 | 403 | 0 | integer | integer
1976 | 23 | 23 | 5 | s | 521 | 403 | 0 | integer | integer
1976 | 23 | 21 | 1 | s | 535 | 403 | 0 | integer | smallint
1976 | 23 | 21 | 2 | s | 541 | 403 | 0 | integer | smallint
1976 | 23 | 21 | 3 | s | 533 | 403 | 0 | integer | smallint
1976 | 23 | 21 | 4 | s | 543 | 403 | 0 | integer | smallint
1976 | 23 | 21 | 5 | s | 537 | 403 | 0 | integer | smallint
1976 | 23 | 20 | 1 | s | 37 | 403 | 0 | integer | bigint
1976 | 23 | 20 | 2 | s | 80 | 403 | 0 | integer | bigint
1976 | 23 | 20 | 3 | s | 15 | 403 | 0 | integer | bigint
1976 | 23 | 20 | 4 | s | 82 | 403 | 0 | integer | bigint
1976 | 23 | 20 | 5 | s | 76 | 403 | 0 | integer | bigint
1976 | 20 | 20 | 1 | s | 412 | 403 | 0 | bigint | bigint
1976 | 20 | 20 | 2 | s | 414 | 403 | 0 | bigint | bigint
1976 | 20 | 20 | 3 | s | 410 | 403 | 0 | bigint | bigint
1976 | 20 | 20 | 4 | s | 415 | 403 | 0 | bigint | bigint
1976 | 20 | 20 | 5 | s | 413 | 403 | 0 | bigint | bigint
1976 | 20 | 21 | 1 | s | 1870 | 403 | 0 | bigint | smallint
1976 | 20 | 21 | 2 | s | 1872 | 403 | 0 | bigint | smallint
1976 | 20 | 21 | 3 | s | 1868 | 403 | 0 | bigint | smallint
1976 | 20 | 21 | 4 | s | 1873 | 403 | 0 | bigint | smallint
1976 | 20 | 21 | 5 | s | 1871 | 403 | 0 | bigint | smallint
1976 | 20 | 23 | 1 | s | 418 | 403 | 0 | bigint | integer
1976 | 20 | 23 | 2 | s | 420 | 403 | 0 | bigint | integer
1976 | 20 | 23 | 3 | s | 416 | 403 | 0 | bigint | integer
1976 | 20 | 23 | 4 | s | 430 | 403 | 0 | bigint | integer
1976 | 20 | 23 | 5 | s | 419 | 403 | 0 | bigint | integer
(45 rows)索引方法系统表之间的联系
下图以 Btree integer_ops 为例,表明“操作符族”与“操作符类”之间的关系:
pg_opfamily与pg_opclass之间的关系比较简单,即pg_class中记录了opclass 属于哪一个 opfamily 的信息。
pg_am、pg_opfamily、pg_amop、pg_amproc 之间的关系可以用下面流程图表示:
PostgreSQL 索引访问方法系统表作用及其联系




