接口
前一篇提到,要使用索引,必须获得索引相关的一些信息。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 直接相关联的一些表的关联图,如下:
pg_am 中存储了系统支持的索引类型,及索引相关属性。
pg_opfamily
存储了 opfamily 和支持的索引的信息。每个 operator family 都是 opeartors 和相关支持例程的集合,这些例程为特定索引类型实现了指定的语义。
在同一 family 下的 operators 是“兼容的”,兼容的方式有访问方法决定。Operator family 的概念允许跨数据类型的运算符与索引一起使用,并使用索引的语义进行推理。
pg_amop
pg_amop 存储有关与索引 operator family 相关的操作符的信息。 operator family 中的每一个 operator 在 pg_amop中都有一行。operator 可以是搜索运算符或排序运算符。 一个 operator 可以出现在多个 family 中,但不能出现在多个搜索位置,也不能出现在一个 family 中的多个排序位置。
pg_amproc
pg_amproc 中存储了索引对应的 opeartor familes 相关的 support procedures 的信息。每个 operator family 的每个 support procedure 有一行记录。
pg_opclass
pg_opclass 定义了索引访问方法的 operator class。 每个 operator class 为特定数据类型的索引列和特定索引访问方法定义语义。 operator class 本质上指定特定的 operator family 适用于特定的可索引列数据类型。该 family 中实际可用于索引列的运算符集是接受列的数据类型作为其左侧输入的运算符。
可以通过查询 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 。
通过以上相关的系统表,可以获取系统中支持哪些索引类型,每种索引类型支持哪些数据类型和操作符,以及操作符对应的处理函数,索引使用中需要用到的各种例程等信息。