在第一篇文章中,我们已经提到访问方法必须提供有关自身的信息。让我们看一下访问方法接口的结构。
属性
访问方法的所有属性都存储在«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)