在第一篇文章中,我们已经提到访问方法必须提供有关自身的信息。让我们看一下访问方法接口的结构。
属性
访问方法的所有属性都存储在«pg_am»表中(“am”代表访问方法)。我们还可以从同一个表中获取可用方法的列表:
postgres=# select * from pg_am; amname | amhandler | amtype --------+-------------+-------- btree | bthandler | i hash | hashhandler | i gist | gisthandler | i gin | ginhandler | i spgist | spghandler | i brin | brinhandler | i (6 rows) postgres=#
虽然顺序扫描可以正确地引用访问方法,但由于历史原因,它不在此列表中。
在PostgreSQL 9.5及更低版本中,每个属性都用«pg_am»表的单独字段表示。从版本9.6开始,使用特殊函数查询属性,并将其分为多个层:
·访问方法属性 - «pg_indexam_has_property»
·特定索引的属性 - «pg_index_has_property»
·索引的各列的属性 - «pg_index_column_has_property»
着眼于未来,访问方法层和索引层会分开:截至目前,基于一种访问方法的所有索引将始终具有相同的属性。
以下四个属性是访问方法的属性(通过«btree»的示例):
postgres=# select a.*, p.name, pg_indexam_has_property(a.oid,p.name) from pg_am a, unnest(array['can_order','can_unique','can_multi_col','can_exclude']) p(name) where a.amname = 'btree' order by a.amname; amname | amhandler | amtype | name | pg_indexam_has_property --------+-----------+--------+---------------+------------------------- btree | bthandler | i | can_order | t btree | bthandler | i | can_unique | t btree | bthandler | i | can_multi_col | t btree | bthandler | i | can_exclude | t (4 rows)
其中:
-can_order。
访问方法使我们能够在创建索引时指定值的排序顺序(目前仅适用于«btree»)。
-can_unique。
支持唯一约束和主键(仅适用于«btree»)。
-can_multi_col。
可以在多个列上构建索引。
-can_exclude。
支持排除约束EXCLUDE。
以下属性与索引有关(例如,考虑现有索引):
postgres=# select p.name, pg_index_has_property('t_a_idx'::regclass,p.name) from unnest(array[ 'clusterable','index_scan','bitmap_scan','backward_scan' ]) p(name); name | pg_index_has_property ---------------+----------------------- clusterable | t index_scan | t bitmap_scan | t backward_scan | t (4 rows)
·clusterable:
根据索引重新排序行的可能性(参看cluster命令)
·index_scan:
支持索引扫描。尽管这个特性看起来有点奇怪,但并不是所有的索引可以逐个范围tid。有些一次性返回所有结果,并且只支持位图扫描。
·bitmap_scan:
支持位图扫描
·backward_scan:
结果的返回顺序可以与构建索引时指定的顺序相反。
最后来看看列的属性
postgres=# select p.name, pg_index_column_has_property('t_a_idx'::regclass,1,p.name) from unnest(array[ 'asc','desc','nulls_first','nulls_last','orderable','distance_orderable', 'returnable','search_array','search_nulls' ]) p(name); name | pg_index_column_has_property --------------------+------------------------------ asc | t desc | f nulls_first | f nulls_last | t orderable | t distance_orderable | f returnable | t search_array | t search_nulls | t (9 rows)
·asc, desc, nulls_first, nulls_last, orderable
这些属性与值的排序有关
·distance_orderable
结果可以按照操作决定的排序顺序返回(目前只是支持GiST和RUM索引)
·returnable
使用索引而不访问表的可能性,即支持index-only 扫描。
·search_array
支持使用表达式«indexed-field IN (list_of_constants)»搜索多个值,它与«indexed-field = ANY(array_of_constants)»相同。
·search_nulls
is null和is not null搜索。
我们已经详细讨论了一些属性。有些属性是特定于某些访问方法的。我们将在考虑这些具体方法时讨论这些性质。
操作符类和操作符家族
除了由接口所描述的公开的访问方法的属性外,还需要了解访问方法接受哪些数据类型和哪些操作符。为此,PostgreSQL引入了操作符类和操作符族的概念。
操作符类包含索引操作特定数据类型的最小操作符集(可能还有辅助函数)。
操作符类包含在某个操作符族中。此外,一个通用操作符族可以包含多个操作符类(如果它们具有相同的语义)。例如,«integer_ops»家族包括«int8_ops»、«int4_ops»和«int2_ops»类,它们的类型«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)
另一个例子:«datetime_ops»家族包含操作日期的操作符类(有时间和没有时间):
postgres=# select opfname, opcname, opcintype::regtype from pg_opclass opc, pg_opfamily opf where opf.opfname = 'datetime_ops' and opc.opcfamily = opf.oid and opf.opfmethod = ( select oid from pg_am where amname = 'btree' ); opfname | opcname | opcintype --------------+-----------------+----------------------------- datetime_ops | date_ops | date datetime_ops | timestamptz_ops | timestamp with time zone datetime_ops | timestamp_ops | timestamp without time zone (3 rows)
操作符族还可以包括其他操作符,用于比较不同类型的值。将操作符类进行分类是为了让计划器为不同类型值的谓词使用索引。一个操作符家族也可以包含辅助函数。
在大多数情况下,我们不需要了解操作符家族和类。通常我们只创建索引,默认情况下使用特定的操作符类。
但是,我们可以显式地指定操作符类。这是一个需要显式规范的简单例子:在与C不同的排序规则的数据库中,常规索引不支持LIKE操作:
postgres=# show lc_collate; lc_collate ------------- en_US.UTF-8 (1 row) postgres=# explain (costs off) select * from t where b like 'A%'; QUERY PLAN ----------------------------- Seq Scan on t Filter: (b ~~ 'A%'::text) (2 rows)
我们可以通过使用操作符类«text_pattern_ops»创建一个索引来克服这个限制(注意计划中的条件是如何变化的):
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)
在本文的最后,我们提供了一个与操作符类和族直接相关的系统目录表的简化图。
系统目录使我们能够在不查看文档的情况下找到许多问题的答案。例如,某个访问方法可以操作哪些数据类型?
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 ...
操作符类包含哪些操作符(以及包含此类操作符的、索引访问可用于条件)?
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) (5 rows)