性能统计与运行时监控
性能统计与运行时监控
本文件包含模块:pg_stat_statements(SQL 统计)、pgstattuple(元组统计)、pg_trgm(三元组匹配)、pg_visibility(可见性映射)、postgres_fdw(PostgreSQL 远程访问)。
pg_stat_statements
pg_stat_statements模块提供了一种跟踪服务器执行的所有 SQL 语句的计划和执行统计信息的方法。
该模块必须通过在postgresql.conf的shared_preload_libraries中增加pg_stat_statements来载入,因为它需要额外的共享内存。这意味着增加或移除该模块要一次服务器重启。
当pg_stat_statements被载入时,它会跟踪该服务器的所有数据库的统计信息。
该模块提供了一个视图pg_stat_statements以及函数pg_stat_statements_rese和pg_stat_statements用于访问和操纵这些统计信息。这些视图和函数不是全局可用的,但是可以用CREATE EXTENSION pg_stat_statements 为特定数据库启用它们。
pg_stat_statements视图
由该模块收集的统计信息可以通过一个名为 pg_stat_statements的视图使用。这个视图为每一个可区分的数据库 ID、用户 ID 和查询 ID(该模块可以追踪的可区分语句的数量)的组合都包含一行。该视图的列,如表 D.21中所示。
表 D.21. pg_stat_statements列
列类型/描述:
userid oid (references pg_authid.oid):执行该语句的用户的 OIDdbid oid (references pg_database.oid):在其中执行该语句的数据库的 OIDqueryid bigint:内部哈希码,从语句的解析树计算得来query text:语句的文本形式plans bigint:计划语句的次数(如果启用了pg_stat_statements.track_planning,否则为零)total_plan_time double precision:计划语句所花费的总时间,以毫秒为单位(如果启用了pg_stat_statements.track_planning,否则为零)min_plan_time double precision:计划语句所花费的最短时间,以毫秒为单位(如果启用了pg_stat_statements.track_planning,否则为零)max_plan_time double precision:计划语句所花费的最长时间,以毫秒为单位(如果启用了pg_stat_statements.track_planning,否则为零)mean_plan_time double precision:计划语句所花费的平均时间,以毫秒为单位(如果启用了pg_stat_statements.track_planning,否则为零stddev_plan_time double precision:计划语句花费的时间的总体标准偏差,以毫秒为单位(如果启用了pg_stat_statements.track_planning,否则为零calls bigint:语句被执行的次数total_exec_time double precision:执行语句所花费的总时间,以毫秒为单位min_exec_time double precision:执行语句所花费的最短时间,以毫秒为单位max_exec_time double precision:执行语句所花费的最长时间,以毫秒为单位mean_exec_time double precision:执行语句的平均时间,以毫秒为单位stddev_exec_time double precision:执行语句花费的时间的总体标准偏差,以毫秒为单位rows bigint:语句检索或影响的总行数shared_blks_hit bigint:语句的共享块缓存命中总数shared_blks_read bigint:语句读取的共享块总数shared_blks_dirtied bigint:被语句弄脏的共享块总数shared_blks_written bigint:语句写入的共享块总数local_blks_hit bigint:语句的本地块缓存命中总数local_blks_read bigint:语句读取的本地块总数local_blks_dirtied bigint:被语句弄脏的本地块总数local_blks_written bigint:语句写入的本地块总数temp_blks_read bigint:语句读取的临时块总数temp_blks_written bigint:语句写入的临时块总数blk_read_time double precision:语句读取块所花费的总时间,以毫秒为单位(如果启用了track_io_timing,否则为零)blk_write_time double precision:语句写入块所花费的总时间,以毫秒为单位(如果启用了track_io_timing,否则为零)wal_records bigint:语句生成的 WAL 记录总数wal_fpi bigint:语句生成的 WAL 整页图像总数wal_bytes numeric:语句生成的 WAL 字节总数
由于安全性原因,只有超级用户和pg_read_all_stats 角色的成员被允许看到其他用户执行的查询的 SQL 文本或者queryid。不过,如果该视图被安装在其他用户的数据库中,那么他们就能够看见统计信息。
只要可规划的查询(即SELECT、INSERT、UPDATE以及DELETE)根据一种内部哈希计算具有相同的查询结构,它们就会被组合到一个单一的pg_stat_statements项。通常,对于这里的目的,如果两个查询除了查询中的文本常量值之外在语义上等效,它们将会被认为是相同的。
不过,功能性命令(即所有其他命令)会严格地以它们的文本查询字符串为基础进行比较。
当把一个查询与其他查询匹配,常数值会被忽略,在pg_stat_statements显示中它会被一个参数符号,比如$1所替换。查询文本的剩余部分就是具有与该pg_stat_statements项相关的特定queryid哈希值的第一个查询的文本。
在某些情况中,具有明显不同文本的查询可能会被融合到一个单一的pg_stat_statements项。通常这只会发生在语义等价的查询身上,但是也有很小的机会因为哈希碰撞的原因导致无关的查询被融合到一个项中(不过,对于属于不同用户或数据库的查询来说不会发生这种情况)。
由于queryid哈希值是根据查询被解析和分析后的表达计算的,对立的情况也可能存在:如果相同文本的查询,由于参数(如不同的search_path设置)的原因而具有不同的含义,它们就可能作为不同的项存在。
pg_stat_statements的使用者可能希望使用 queryid(也许会与dbid和userid组合)作为一个项比查询文本更稳定和可靠的标识符。但是,有一点很重要的是,对于queryid哈希值稳定性是有限的保障。因为该标识符是从解析分析后的树得来的,它的值是以这种形式出的内部对象标识符的函数。这有一些违背直觉的含义。例如,有两个查询引用了同一个表,但是该表在两次查询之间被删除并且重建,显然这两个查询是完全一致的,但是pg_stat_statements将把它们认为是不同的。哈希处理也对机器架构以及平台的其他方的差别很敏感。
两台复制中的服务器参与物理 WAL 重放,会对相同的查询给出一样的queryid值。但是,逻辑复制模式并不保证在所有相关细节上都保持完全一样的复制,因此在逻辑复制机之间计算代价时,queryid并非是一个有用的标识符。
代表性查询文本中用于替换常量的参数符号,从原始查询文本中最高的 $n参数之后的下一个数字开始,如果没有则为$1。值得注意的是,在某些情况下,可能存在影响编号的隐藏参数符号。
有代表性的查询文本被保存在一个外部磁盘文件中,并且不会消耗共享内存。 因此,即便是很长的查询文本也能被成功的存储下来。不过,如果累积了很多长的查询文本,该外部文件也会增长到很大。作为一种恢复方法,如果这样的情况发生,pg_stat_statements可能会选择丢弃这些查询文本,于是pg_stat_statements视图中的所有现有项将会显示空的query域,不过与每个queryid相关联的统计信息会被保留下来。如果发生这种情况,可以考虑减小 pg_stat_statements.max来防止复发。
plans和calls并不总是匹配的,因为计划和执行统计信息在它们各自的结束阶段更新,并且仅适用于成功的操作。例如,如果一条语句计划成功,但在执行阶段失败,则只会更新其计划统计信息。如果因为使用了缓存计划而跳过计划,则只会更新其执行统计信息。
函数
pg_stat_statements_reset(userid Oid, dbid Oid, queryid bigint) returns void
pg_stat_statements_reset丢弃到目前为止与指定的userid, dbid和queryid相对应的pg_stat_statements收集的统计信息。 如果有任何参数未被指定,那么将对这些参数使用默认值0(无效),并且将重置与其他参数匹配的统计信息。如果未指定任何参数,或者所有指定的参数均为0(无效),则它将丢弃所有统计信息。 默认情况下,此功能只能由超级用户执行。可以使用GRANT授予其他人访问权限。
pg_stat_statements(showtext boolean) returns setof record
pg_stat_statements视图按照一个也叫 pg_stat_statements的函数来定义。客户端可以直接调用 pg_stat_statements函数,并且通过指定 showtext := false来忽略查询文本(即,对应于视图的 query列的OUT参数将返回空值)。这个特性是为了支持不想重复接收长度不定的查询文本,设计的外部工具。这类工具可以转而自行缓存第一个观察到的查询文本,因为这就是 pg_stat_statements自己所做的全部工作,并且只在需要的时候检索查询文本。因为服务器会把查询文本存储在一个文件中,这种方法可以降低重复检查pg_stat_statements数据的物理 I/O。
配置参数
pg_stat_statements.max (integer)
pg_stat_statements.max是由该模块跟踪的最大数目语句(即pg_stat_statements视图中行的最大数量)。如果观测到的可区分的语句超过这个数量,最少被执行的语句的信息将会被丢弃。默认值为 5000。这个参数只能在服务器启动时设置。
pg_stat_statements.track (enum)
pg_stat_statements.track控制哪些语句会被该模块计数。指定top可以跟踪顶层语句(那些直接由客户端发出的语句),指定all还可以跟踪嵌套的语句(例如在函数中调用的语句),指定none可以禁用语句统计信息收集。默认值是top。只有超级用户能够改变这个设置。
pg_stat_statements.track_utility (boolean)
pg_stat_statements.track_utility控制该模块是否会跟踪工具命令。工具命令是除了SELECT、INSERT、 UPDATE和DELETE之外所有的其他命令。默认值是on。只有超级用户能够改变这个设置。
pg_stat_statements.track_planning (boolean)
pg_stat_statements.track_planning控制模块是否跟踪计划操作和持续时间。 启用此参数可能会导致明显的性能损失,尤其是在许多并发连接上执行较少种类的查询时。默认值为off。只有超级用户才能更改此设置。
pg_stat_statements.save (boolean)
pg_stat_statements.save指定是否在服务器关闭之后还保存语句统计信息。如果被设置为off,那么关闭后不保存统计信息并且在服务器启动时也不会重新载入统计信息。默认值为on。这个参数只能在postgresql.conf文件中或者在服务器命令行上设置。
该模块要求与pg_stat_statements.max成比例的额外共享内存。
注意:只要该模块被载入就会消耗这么多的内存,即便pg_stat_statements.track被设置为none。
这些参数必须在postgresql.conf中设置。典型的用法可能是:
# postgresql.conf
shared_preload_libraries = 'pg_stat_statements'
pg_stat_statements.max = 10000
pg_stat_statements.track = all
示例输出
bench=# SELECT pg_stat_statements_reset();
\$ pgbench -i bench
\$ pgbench -c10 -t300 bench
bench=# \\x
bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------
------------------
query | UPDATE pgbench_branches SET bbalance = bbalance + \$1 WHERE bid
= \$2
calls | 3000
total_exec_time | 25565.855387
rows | 3000
hit_percent | 100.0000000000000000
-[ RECORD 2 ]---+--------------------------------------------------
------------------
query | UPDATE pgbench_tellers SET tbalance = tbalance + \$1 WHERE tid
= \$2
calls | 3000
total_exec_time | 20756.669379
rows | 3000
hit_percent | 100.0000000000000000
-[ RECORD 3 ]---+--------------------------------------------------
------------------
query | copy pgbench_accounts from stdin
calls | 1
total_exec_time | 291.865911
rows | 100000
hit_percent | 100.0000000000000000
-[ RECORD 4 ]---+--------------------------------------------------
------------------
query | UPDATE pgbench_accounts SET abalance = abalance + \$1 WHERE aid
= \$2
calls | 3000
total_exec_time | 271.232977
rows | 3000
hit_percent | 98.8454011741682975
-[ RECORD 5 ]---+--------------------------------------------------
------------------
query | alter table pgbench_accounts add primary key (aid)
calls | 1
total_exec_time | 160.588563
rows | 0
hit_percent | 100.0000000000000000
=# SELECT pg_stat_statements_reset(0,0,s.queryid) FROM pg_stat_statements
AS s
WHERE s.query = 'UPDATE pgbench_branches SET bbalance = bbalance +
\$1 WHERE bid = \$2';
bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------
------------------
query | UPDATE pgbench_tellers SET tbalance = tbalance + \$1 WHERE tid
= \$2
calls | 3000
total_exec_time | 20756.669379
rows | 3000
hit_percent | 100.0000000000000000
-[ RECORD 2 ]---+--------------------------------------------------
------------------
query | copy pgbench_accounts from stdin
calls | 1
total_exec_time | 291.865911
rows | 100000
hit_percent | 100.0000000000000000
-[ RECORD 3 ]---+--------------------------------------------------
------------------
query | UPDATE pgbench_accounts SET abalance = abalance + \$1 WHERE aid
= \$2
calls | 3000
total_exec_time | 271.232977
rows | 3000
hit_percent | 98.8454011741682975
-[ RECORD 4 ]---+--------------------------------------------------
------------------
query | alter table pgbench_accounts add primary key (aid)
calls | 1
total_exec_time | 160.588563
rows | 0
hit_percent | 100.0000000000000000
-[ RECORD 5 ]---+--------------------------------------------------
------------------
query | vacuum analyze pgbench_accounts
calls | 1
total_exec_time | 136.448116
rows | 0
hit_percent | 99.9201915403032721
bench=# SELECT pg_stat_statements_reset(0,0,0);
bench=# SELECT query, calls, total_exec_time, rows, 100.0 * shared_blks_hit /
nullif(shared_blks_hit + shared_blks_read, 0) AS hit_percent
FROM pg_stat_statements ORDER BY total_exec_time DESC LIMIT 5;
-[ RECORD 1 ]---+--------------------------------------------------
---------------------------
query | SELECT pg_stat_statements_reset(0,0,0)
calls | 1
total_exec_time | 0.189497
rows | 1
hit_percent |
-[ RECORD 2 ]---+--------------------------------------------------
---------------------------
query | SELECT query, calls, total_exec_time, rows, \$1 *
shared_blks_hit / +
| nullif(shared_blks_hit + shared_blks_read, \$2)
AS hit_percent+
| FROM pg_stat_statements ORDER BY total_exec_time
DESC LIMIT \$3
calls | 0
total_exec_time | 0
rows | 0
hit_percent |
pgstattuple
pgstattuple模块提供多种函数来获得元组层的统计信息。
由于这些函数返回详细的页面级信息,因此默认访问权限是受限制的。 默认情况下,只有角色pg_stat_scan_tables具有EXECUTE特权。 超级用户当然可以绕过这个限制。扩展程序安装后,用户可以发送GRANT命令来更改函数的权限以允许其他用户来执行它们。但是,最好将这些用户添加到pg_stat_scan_tables角色。
函数
pgstattuple(regclass) 返回 record
pgstattuple返回一个关系的物理长度、元组的百分比以及其他信息。这可以帮助用户决定是否需要清理。
参数是目标关系的名称(可以有选择地用模式限定)或者OID。例如:
test=# \x on
扩展显示已打开
test=# SELECT * FROM pgstattuple('pg_catalog.pg_proc');
-[ RECORD 1 ]------+-------
table_len | 876544
tuple_count | 3558
tuple_len | 815847
tuple_percent | 93.08
dead_tuple_count | 18
dead_tuple_len | 7891
dead_tuple_percent | 0.9
free_space | 22168
free_percent | 2.53
表 D.22中描述了输出列。
表 D.22. pgstattuple 输出列
----------------------- ---------------------- ----------------------------
列 类型 描述
table_len bigint 物理关系长度,以字节计
tuple_count bigint 存活元组的数量
tuple_len bigint 存活元组的总长度,以字节计
tuple_percent float8 存活元组的百分比
dead_tuple_count bigint 死亡元组的数量
dead_tuple_len bigint 死亡元组的总长度,以字节计
dead_tuple_percent float8 死亡元组的百分比
free_space bigint 空闲空间总量,以字节计
free_percent float8 空闲空间的百分比
----------------------- ---------------------- ----------------------------
注意:table_len将总是比tuple_len、 dead_tuple_len和free_space的和要大。 不同之处在于固定的页面开销,每页指向元组的指针表和填充以确保元组正确对齐。
pgstattuple只要求在关系上的一个读锁。因此结果不能反映一个即时快照,并发更新将影响结果。
如果HeapTupleSatisfiesDirty返回假,pgstattuple就判定一个元组是"死亡的"。
pgstattuple(text) 返回 record
与pgstattuple(regclass)相同,只不过通过 TEXT 指定目标关系。这个函数只是为了向后兼容而保留,在未来的发布中将会被废除。
pgstatindex(regclass) 返回 record
pgstatindex返回一个记录显示有关一个 B-树索引的信息。例如:
test=# \x on
扩展显示已打开
test=# SELECT * FROM pgstatindex('pg_cast_oid_index');
-[ RECORD 1 ]------+------
version | 4
tree_level | 0
index_size | 16384
root_block_no | 1
internal_pages | 0
leaf_pages | 1
empty_pages | 0
deleted_pages | 0
avg_leaf_density | 62.37
leaf_fragmentation | 0
输出列是:
----------------------- ---------------------- ------------------------------
列 类型 描述
version integer B-树版本号
tree_level integer 根页的树层次
index_size bigint 以字节计的索引总尺寸
root_block_no bigint 根页的位置(如果没有则为零)
internal_pages bigint "内部"(上层)页面的数量
leaf_pages bigint 叶子页的数量
empty_pages bigint 空页的数量
deleted_pages bigint 删除页的数量
avg_leaf_density float8 叶子页的平均密度
leaf_fragmentation float8 叶子页碎片
----------------------- ---------------------- ------------------------------
报告的index_size通常对应于internal_pages + leaf_pages + empty_pages +deleted_pages加一,因为它还包括索引的元页。
对于pgstattuple,结果是一页一页累计的并且不要期望结果会表示整个索引的一个即时快照。
pgstatindex(text) returns record与pgstatindex(regclass)相同,只不过通过 TEXT 指定目标索引。这个函数只是为了向后兼容而保留,在未来的某个发布中将会被废除。
pgstatginindex(regclass) 返回 record
pgstatginindex返回一个记录显示有关一个 GIN 索引的信息。例如:
test=> SELECT * FROM pgstatginindex('test_gin_index');
-[ RECORD 1 ]--+--
Version | 1
pending_pages | 0
pending_tuples | 0
输出列是:
----------------------- ---------------------- ----------------------
列 类型 描述
version integer GIN 版本号
pending_pages integer 待处理列表中的页面数
pending_tuples bigint 待处理列表中的元组数
----------------------- ---------------------- ----------------------
pgstathashindex(regclass) returns record
pgstathashindex返回一个显示HASH索引信息的记录。例如:
test=# select * from pgstathashindex('con_hash_index');
-[ RECORD 1 ]--+-----------------
Version | 4
bucket_pages | 33081
overflow_pages | 0
bitmap_pages | 1
unused_pages | 32455
live_items | 10204006
dead_items | 0
free_percent | 61.8005949100872
输出字段是:
----------------------- ---------------------- ----------------------
字段 类型 描述
version integer HASH版本号
bucket_pages bigint 存储桶页面的数量
overflow_pages bigint 溢出页面的数量
bitmap_pages bigint 位图页数
unused_pages bigint 未使用页面的数量
live_items bigint 活元组的数量
dead_tuples bigint 死元组的数量
free_percent float 自由空间的百分比
----------------------- ---------------------- ----------------------
pg_relpages(regclass) 返回 bigint
pg_relpages返回关系中的页面数。
pg_relpages(text) returns bigint
与pg_relpages(regclass)相同,只不过用 TEXT 来指定目标关系。这个函数只是为了向后兼容而保留。
pgstattuple_approx(regclass) returns record
pgstattuple_approx是pgstattuple的一个更加快速的替代品,它返回近似的结果。参数是目标关系的 OID 或者名称。例如:
test=# \x on
扩展显示已打开
test=# SELECT * FROM pgstattuple_approx('pg_catalog.pg_proc'::regclass);
-[ RECORD 1 ]--------+-------------------
table_len | 876544
scanned_percent | 14.953271028037383
approx_tuple_count | 3510
approx_tuple_len | 843334
approx_tuple_percent | 96.21125693633178
dead_tuple_count | 11
dead_tuple_len | 4675
dead_tuple_percent | 0.533344589661215
approx_free_space | 24320
approx_free_percent | 2.774532710280374
输出列在表 D.23中描述。
鉴于pgstattuple总是执行全表扫描并且返回存活和死亡元组的准确计数、尺寸和空闲空间,pgstattuple_approx尝试避免全表扫描并且返回死亡元组的准确统计信息,以及存活元组和空闲空间的近似数量及尺寸。
这个函数通过根据可见性映射跳过只包含可见元组的页面来实现这一目的(如果一个页面对应的 VM 位被设置,那么就说明它不含有死亡元组)。对于这样的额页面,它会从空闲空间映射中得到空闲空间值,并且假定该页面上的剩余空间由存活元组占据。
对于不能被跳过的页面,它会扫描每个元组,在合适的计数器中记录它的存在以及尺寸,并且统计该页面上的空闲空间。最后,它会基于已扫描的页面和元组数量来估计存活元组的总数(采用与 VACUUM 估计 pg_class.reltuples 时相同的方法)。
表 D.23. pgstattuple_approx输出列
----------------------- ---------------------- ----------------------------------
列 类型 描述
table_len bigint 以字节计的物理关系长度(准确)
scanned_percent float8 已扫描表的百分比
approx_tuple_count bigint 存活元组的数量(估计)
approx_tuple_len bigint 以字节计的存活元组总长度(估计)
approx_tuple_percent float8 存活元组的百分比
dead_tuple_count bigint 死亡元组的数量(准确)
dead_tuple_len bigint 以字节计的死亡元组总长度(准确)
dead_tuple_percent float8 死亡元组的百分比
approx_free_space bigint 以字节计的总空闲空间(估计)
approx_free_percent float8 空闲空间的百分比
----------------------- ---------------------- ----------------------------------
在上述的输出中,空闲空间数字可能不完全匹配pgstattuple的输出,这是因为空闲空间映射会给出一个准确的数字,但是这个数字不能保证是一个准确的字节数。
pg_trgm
pg_trgm模块提供用于决定基于 trigram 匹配的字母数字文本相似度的函数和操作符,以及支持快速搜索相似字符串的索引操作符类。
该模块被认为是"trusted",也就是说,它可以由对当前数据库具有CREATE权限的非超级用户安装。
Trigram(或者 Trigraph)概念
一个 trigram 是从一个字符串中取出的,由三个连续字符组成的组。我们可以通过对两个字符串之间共享的 trigram 计数来度量它们的相似度。这种简单的思想已经成为在很多自然语言中度量词相似度的有效方法。
注意:在从一个字符串中提取 trigram 时,pg_trgm会忽略非词字符(非字母数字)。在决定字符串中所含的 trigram 集合时,每一个词被认为具有两个空格前缀和一个空格后缀。例如,字符串"cat"中的 trigram 集合是: " c"、 " ca"、 "cat"以及"at "。 字符串"foo\
函数和操作符
pg_trgm模块所提供的函数如表 D.24中所示,操作符则显示在表 D.25中。
表 D.24. pg_trgm函数
函数/描述:
similarity ( text, text ) → real:返回一个数字指示两个参数有多相似。该结果的范围是 0(指示两个字符串完全不相似)到 1(指示两个字符串完全一样)。show_trgm ( text ) → text[]:返回一个给定字符串中所有的 trigram 组成的一个数组(实际上除了调试很少有用)。word_similarity ( text, text ) → real:返回一个数字表示第一个字符串中的trigram集合与第二个字符串中trigram的有序集中任何连续部分的最大相似度。strict_word_similarity ( text, text ) → real:与word_similarity(text, text)相同,但是强制连续部分的边界与词边界相配。由于我们没有跨词的trigram,这个函数实际上返回第一个字符串和第二个字符串任意连续部分的相似度。show_limit () → real:返回%操作符使用的当前相似度阈值。例如,这设定两个词被认为足够相似时,它们之间应满足的最小相似度(已废弃;而是使用SHOW pg_trgm.similarity_threshold)。set_limit ( real ) → real:设定%操作符使用的当前相似度阈值。该阈值必须介于 0 和 1 之间(默认为 0.3)。 返回传递进来的同一个值(已废弃;而是使用SET pg_trgm.similarity_threshold)
考虑下面的例子:
test=# SELECT word_similarity('word', 'two words');
word_similarity
-----------------
0.8
(1 row)
在第一个字符串中,trigram集合是{" w"," wo","wor","ord","rd "}。在第二个字符串中,trigram的有序集是{" t"," tw","two","wo "," w"," wo","wor","ord","rds","ds "}。
在第二个字符串中最相似的trigram有序集的部分是{" w"," wo","wor","ord"},并且相似度是0.8。
这个函数返回的值可以大概地理解为第一个字符串和第二个字符串任意子串的最大相似度。
不过,这个函数不会对该部分的边界加入填充。因此,除了失配的词边界之外,第二个字符串中存在的额外字符的数目没有被考虑。
同时,strict_word_similarity在第二个字符串中选择一个由词构成的部分。 在上面的例子中,strict_word_similarity会选择单个词'words'形成的部分, 其trigram集合为{"w"," wo","wor","ord","rds","ds "}。
test=# SELECT strict_word_similarity('word', 'two words'), similarity('word', 'words');
strict_word_similarity | similarity
------------------------+------------
0.5714286 | 0.5714286
(1 row)
因此,strict_word_similarity函数对于计算整个词的相似度有用,而word_similarity更适合于计算词的部分相似度。
表 D.25. pg_trgm操作符
操作符/描述:
text % text → boolean:如果参数具有超过pg_trgm.similarity_threshold设置的相似度阈值,则返回true。text <% text → boolean:如果第一个参数中的trigram集合与第二个参数中有序trigram集合的一个连续部分之间的相似度超过 pg_trgm.word_similarity_threshold参数设置的相似度阈值,则返回true。text %> text → boolean:<%操作符的交换子。text <<% text → boolean:如果第二个参数有序trigram集合的一个连续部分匹配词边界,并且其与第一个参数的trigram集合的相似度超过pg_trgm.strict_word_similarity_threshold参数设置相似度阈值,则返回true。text %>> text → boolean:<<%操作符的交换子。text <-> text → real:返回参数之间的"距离",即 1 减去similarity()值。text <<-> text → real:返回参数之间的"距离",它是 1 减去word_similarity()的值。text <->> text → real:<<->操作符的交换子。text <<<-> text → real:返回参数之间的"距离",也就是1减去strict_word_similarity()的值。text <->>> text → real:<<<->操作符的交换子。
GUC 参数
pg_trgm.similarity_threshold (real)
设置%操作符使用的当前相似度阈值。该阈值必须位于 0 和 1 之间(默认是 0.3)。
pg_trgm.word_similarity_threshold (real)
设置<%和%>操作符使用的当前词相似度阈值。该阈值必须位于 0 和 1 之间(默认是0.6)。
pg_trgm.strict_word_similarity_threshold (real)
设置<<%和%>>运算符使用的当前严格单词相似性阈值。阈值必须介于0和1之间(默认值为0.5)。
索引支持
pg_trgm模块提供了 GiST 和 GIN 索引操作符类,这允许你在一个文本列上创建索引用于快速相似度搜索的目的。这些索引类型支持上述的相似度操作符,并且额外支持基于 trigram的索引,搜索用于LIKE、ILIKE、~和~*查询(这些索引不支持等值或简单比较操作符,因此你可能还需要一个常规的B-树索引)。
例子:
CREATE TABLE test_trgm (t text);
CREATE INDEX trgm_idx ON test_trgm USING GIST (t gist_trgm_ops);
或CREATE INDEX trgm_idx ON test_trgm USING GIN (t gin_trgm_ops);
gist_trgm_ops GiST opclass 将一组三元组近似为位图签名。它的可选整数参数siglen确定签名长度(以字节为单位)。默认长度为 12 个字节。签名长度的有效值介于 1 到2024 字节之间。更长的签名导致更精确的搜索(扫描索引的一小部分和更少的堆页面),但代价是更大的索引。
创建签名长度为 32 字节的此类索引的示例:
CREATE INDEX trgm_idx ON test_trgm USING GIST
(t gist_trgm_ops(siglen=32));
此时,你将有一个t列上的索引,你可以用它进行相似度搜索。一个典型的查询是
test=# SELECT t, similarity(t, 'word') AS sml
test-# FROM test_trgm
test-# WHERE t % 'word'
test-# ORDER BY sml DESC, t;
t | sml
---+-----
(0 rows)
这将返回在文本列中与word足够相似的所有值,按最佳匹配到最差匹配的方式排序。索引将被用来让这种搜索变快,即使在一个非常大的数据集上。
上述查询的一种变体是
test=# SELECT t, t <-> 'word' AS dist FROM test_trgm
test-# ORDER BY dist LIMIT 10;
t | dist
---+------
(0 rows)
这能够用 GiST 索引有效地实现,但是用 GIN 索引无法做到。当只想要少数最接近的匹配时,这通常会比第一种形式更好。
也可以把一个t列上的索引用于词相似度或者严格词相似度。典型的查询是:
test=# SELECT t, word_similarity('word', t) AS sml
test-# FROM test_trgm
test-# WHERE 'word' <% t
test-# ORDER BY sml DESC, t;
t | sml
---+-----
(0 rows)
和test=# SELECT t, strict_word_similarity('word', t) AS sml
test-# FROM test_trgm
test-# WHERE 'word' <<% t
test-# ORDER BY sml DESC, t;
t | sml
---+-----
(0 rows)
这将返回文本列中符合条件的所有值:这些值在其对应的有序trigram集中有一个连续部分与word的trigram集合足够相似,这些值会按照最好匹配到最差匹配的顺序排列。即便在非常大的数据集上,索引也将使得这一操作的速度够快。
上述查询可能的变体有:
test=# SELECT t, 'word' <<-> t AS dist
test-# FROM test_trgm
test-# ORDER BY dist LIMIT 10;
t | dist
---+------
(0 rows)
和test=# SELECT t, 'word' <<<-> t AS dist
test-# FROM test_trgm
test-# ORDER BY dist LIMIT 10;
t | dist
---+------
(0 rows)
这可以用 GiST 索引很高效地实现,但是用 GIN 索引不行。
例如:
SELECT * FROM test_trgm WHERE t LIKE '%foo%bar';
该索引搜索通过从搜索字符串中抽取 trigram 并且在索引中查找它们来工作。搜索字符串中有更多 trigram,索引搜索的效率更高。不像基于 B-树的搜索,搜索字符串不需要是左锚的。
例如:
SELECT * FROM test_trgm WHERE t ~ '(foo|bar)';
该索引搜索通过从正则表达式中抽取 trigram 并且在索引中查找它们来工作。正则表达式中能抽取出更多 trigram,索引搜索的效率更高。不像基于 B-树的搜索,搜索字符串不需要是左锚定的。
对于LIKE和正则表达式搜索,记住没有可抽取 trigram 的模式将退化成一个全索引扫描。
GiST 和 GIN 索引之间的选择依赖于 GiST 和 GIN 的相对性能特性,这在其他地方讨论。
文本搜索集成
在与一个全文索引联合使用时,trigram 匹配是一种非常有用的工具。特别是它能有助于识别拼写错误的输入词,这些词直接用全文搜索机制是不会被匹配的。
第一步是生成一个包含文档中所有唯一词的辅助表:
CREATE TABLE words AS SELECT word FROM
ts_stat('SELECT to_tsvector(''simple'', bodytext) FROM documents');
其中documents是一个具有我们希望搜索的文本域bodytext的表。对to_tsvector函数使用simple配置而不是使用语言相关的配置的原因是,我们想要一个原始(没有去掉词根的)词的列表。
接下来,在词列上创建一个 trigram 索引:
CREATE INDEX words_idx ON words USING GIN(word gin_trgm_ops);
现在,类似于前面例子的一个SELECT查询可以被用来为用户搜索术语中的拼写不当的词建议拼写。要求被选择的词也与拼写不当的词具有相似的长度是一种有用的额外测试。
注意:由于words表已经被生成为一个单独的、静态的表,它将需要被定期地重新生成,这样它能合理地与文档集合保持一致。但是要求它完全与文档集合同步通常是不必要的。
pg_visibility
pg_visibility模块提供了一种方式来检查一个表的可见性映射(VM)以及页级别的可见性信息。它还提供了函数来检查可见性映射的完整性以及强制重建可见性映射。
有三个不同的位被用来存储有关页级别可见性的信息。可见性映射中的"全部可见"位表示一个关系的对应页面上的所有元组对每一个当前和未来事务都可见。可见性映射中的"全部冻结"位表示该页上的每一个元组都被冻结,也就是说直到在那个页面上对一个元组进行插入、更新、删除或者锁定之前都不需要用 vacuum 对该页面进行修改。页面头部的PD_ALL_VISIBLE位具有和可见性映射中"全部可见"位相同的含义, 但是它存储在数据页面本身中而不是存储在单独的数据结构中。这两个位通常是相互一致的,但是有时在崩溃恢复后会出现页面的"全部可见"位被设置而可见性映射位被清除的情况,由于在pg_visibility检查了可见性映射之后, 且在它检查数据页面之前发生了修改,报告的值也会不一致。任何导致数据损坏的事件也可能导致这些位不一致。
显示有关PD_ALL_VISIBLE的信息的函数代价比那些查看可见性映射的函数要高很多,因为它们必须读取关系的数据块而不是只读取(小很多的)可见性映射。类似地,检查关系的数据块的函数也很昂贵。
函数
pg_visibility_map(relation regclass, blkno bigint, all_visible OUT boolean,all_frozen OUT boolean) returns record
为给定关系的给定块返回其在可见性映射中的"全部可见"和"全部冻结"位。
pg_visibility(relation regclass, blkno bigint, all_visible OUT boolean, all_frozen
OUT boolean, pd_all_visible OUT boolean) returns record
为给定关系的给定块返回其在可见性映射中的"全部可见"和"全部冻结"位,外加块的PD_ALL_VISIBLE位。
pg_visibility_map(relation regclass, blkno OUT bigint, all_visible OUT boolean,all_frozen OUT boolean) returns setof record
为给定关系的每一块返回其在可见性映射中的"全部可见"和"全部冻结"位。
pg_visibility(relation regclass, blkno OUT bigint, all_visible OUT boolean,all_frozen OUT boolean, pd_all_visible OUT boolean) returns setof record
为给定关系的每一块返回其在可见性映射中的"全部可见"和"全部冻结"位,外加每一块的PD_ALL_VISIBLE位。
pg_visibility_map_summary(relation regclass, all_visible OUT bigint, all_frozen OUT bigint) returns record
根据可见性映射返回关系中"全部可见"页面和"全部冻结"页面的数量。
pg_check_frozen(relation regclass, t_ctid OUT tid) returns setof tid
返回存储在可见性映射中被标为"全部冻结"的页面中的非冻结元组的 TID。 如果这个函数返回一个非空的 TID 集合,则可见性映射已经损坏。
pg_check_visible(relation regclass, t_ctid OUT tid) returns setof tid
返回存储在可见性映射中标记为全部可见的页面中的非全部可见元组的TID。 如果此函数返回非空的一组TID,则可见性映射已损坏。
pg_truncate_visibility_map(relation regclass) returns void
为给定关系截断可见性映射。如果您认为该关系的可见性映射已损坏并希望强制重建它,则该函数非常有用。在这个函数被执行后,在给定关系上进行的第一次VACUUM 将会扫描关系中的每一个页面并且重建可见性映射。(在完成之前,查询会将可见性映射看做包含的全是零。)
默认情况下,这些函数只有超级用户和pg_stat_scan_tables角色的成员可以执行,除了pg_truncate_visibility_map(relation regclass)只能由超级用户执行之外。
postgres_fdw
postgres_fdw模块提供了外部数据包装器postgres_fdw,它可以被用来访问存储在外部Halo服务器中的数据。
这个模块提供的功能大体上覆盖了较老的dblink模块的功能。但是postgres_fdw提供了更透明且更兼容标准的语法来访问远程表,并且可以在很多情况下给出更好的性能。
要使用postgres_fdw来为远程访问做准备:
1. 使用CREATE EXTENSION来安装postgres_fdw扩展。
2. 使用CREATE SERVER创建一个外部服务器对象,它用来表示你想连接的每一个远程数据库。指定除了user和password之外的连接信息作为该服务器对象的选项。
3. 使用CREATE USER MAPPING创建一个用户映射,每一个用户映射都代表你想允许一个数据库用户访问一个外部服务器。指定远程用户名和口令作为用户映射的user和password选项。
4. 为每一个你想访问的远程表使用CREATE FOREIGN TABLE或者IMPORT FOREIGN SCHEMA创建一个外部表。外部表的列必须匹配被引用的远程表。但是,如果你在外部表对象的选项中指定了正确的远程名称,你可以使用不同于远程表的表名和/或列名。
现在你只需要从一个外部表SELECT来访问存储在它的底层的远程表中的数据。你也可以使用INSERT、UPDATE或DELETE修改远程表(当然,你在你的用户映射中已经指定的远程用户必须具有做这些事情的权限)。
注意当前postgres_fdw缺少对于带ON CONFLICT DO UPDATE子句的INSERT语句的支持。不过,它支持ON CONFLICT DO NOTHING子句,已提供的唯一索引推断说明会被省略。另请注意postgres_fdw支持 UPDATE 语句在分区表上执行的行移动调用,但是它目前不处理这样的
情况:选择将移动行插入到的远程分区也是稍后将更新的UPDATE目标分区。
我们通常推荐一个外部表的列被声明为与被引用的远程表列完全相同的数据类型和排序规则(如果可用)。尽管postgres_fdw目前已经能够容忍在需要时执行数据类型转换,但是当类型或排序规则不匹配时可能会发生奇怪的语义异常,因为远程服务器解释WHERE子句时可能会与本地服务器有所不同。
注意:一个外部表可以被声明比底层的远程表较少的列,或者使用一种不同的列序。与远程表的列匹配是通过名字而不是位置进行的。
postgres_fdw 的 FDW 选项
D.33.1.1. 连接选项
一个使用postgres_fdw外部数据包装器的外部服务器可以使用和libpq在连接字符串中能接受的选项,除了这些选项不被允许或有特殊处理:
• user、password和sslpassword(应该在用户映射中指定这些,或者使用服务文件)
• client_encoding(这是自动从本地服务器编码设置)
• fallback_application_name(总是设置为postgres_fdw)
• sslkey和sslcert - 这些可能出现在either or both连接和用户映射中。如果两者都存在,则用户映射设置会覆盖连接设置。
只有超级用户可以使用sslcert或sslkey设置创建或修改用户映射。
只有超级用户可以在不经过口令认证的情况下连接到外部服务器,因此应总是为属于非超级用户的用户映射指定password选项。
超级用户可以通过设置用户映射选项password_required 'false'在每个用户映射的基础上覆盖此检查,例如,
ALTER USER MAPPING FOR some_non_superuser SERVER loopback_nopw OPTIONS (ADD password_required 'false');
为了防止非特权用户利用正在运行的 postgres 服务器升级到超级用户权限的 unix 用户的身份验证权限,只有超级用户可以在用户映射上设置此选项。
需要注意确保这不允许映射用户能够根据 CVE-2007-3278 和 CVE-2007-6601 作为超级用户连接到映射数据库。不要在public角色上设置password_required=false。 请记住,映射的用户可能会使用 postgres 服务器运行的系统用户的unix 主目录中的任何客户端证书, .pgpass、.pg_service.conf等。 他们还可以使用由诸如peer或ident身份验证等身份验证模式授予的任何信任关系。
D.33.1.2. 对象名称选项
当一个外部表被使用不同于底层远程表的名称创建时,就需要这些选项。
schema_name
这个选项给出用在远程服务器之上的外部表的模式名称,它可以为一个外部表指定。如果这个选项被忽略,该外部表的模式名称将被使用。
table_name
这个选项给出用在远程服务器上的外部表给出表名,它可以为一个外部表指定。如果这个选项被忽略,该外部表的名字将被使用。
column_name
这个选项给出用在远程服务器上列的列名,它可以为一个外部表的一个列指定。如果这个选项被忽略,该列的名字将被使用。
D.33.1.3. 代价估计选项
postgres_fdw通过在远程服务器上执行查询来检索远程数据,因此理想的扫描一个外部表的代价应该是在远程服务器上完成它的花销,外加一些通信开销。得到这样一个可靠的方法是询问远程服务器并加上一些通信开销 --- 但是对于简单查询,不值得为获得一个远程查询而额外使用一次。因此postgres_fdw提供了下列选项来控制如何完成代价估计:
use_remote_estimate
这个选项控制postgres_fdw是否发出EXPLAIN命令来获得代价估计,它可以为一个外部表或一个外部服务器指定。一个外部表的设置会覆盖它的服务器的任何设置,但是只用于这个表。默认值是false。
fdw_startup_cost
这个选项是一个要被加到服务器上所有外部表扫描的估计启动代价的数字值。这表示建立一个连接、在远端解析和规查询的额外负荷等。默认值是100。
fdw_tuple_cost
这个选项是一个数字值,它被用作那个服务器上外部表扫描的每元组额外代价,它可以为一个外部服务器指定。这表示在服务器之间数据传输的额外负荷。可以增加或减少这个数来反映到远程服务器更高或更低的网络延迟。默认值是0.01。
当use_remote_estimate为真时,postgres_fdw从远程服务器获得行计数和代价估计,然后在代价估计上加上fdw_startup_cost和fdw_tuple_cost。当use_remote_estimate为假时,postgres_fdw执行本地行计数和代价估计,并且接着在代价估计上加上fdw_startup_cost 和fdw_tuple_cost。这种本地估计不会很准确,除非有远程表统计数据的本地拷贝可用。在外部表上运行ANALYZE是更新本地统计数据的方法,这将执行远程表的一次扫描并接着计算和存储统计数据,就像表在本地一样。保留本地统计数据可能是一种有用的方法来减少一个远程表的预查询规划负荷 --- 但是如果远程表被频繁更新,本地统计数据将很快就被废弃。
D.33.1.4. 远程执行选项
默认情况下,只有使用了内建操作符和函数的WHERE子句才会被考虑在远程服务器上执行。
涉及非内建函数的子句将会在取完行后在本地进行检查。如果这类函数在远程服务器上可用并且可以用来产生和本地执行时一样的结果,则可以通过将这种WHERE子句发送到远程执行来提高性能。可以用下面的选项控制这种行为:
extensions
属于一个该列表中扩展的 immutable 函数和操作符将被考虑转移到远程服务器上执行。这个选项只能为外部服务器指定,无法逐个表指定。
在使用extensions选项时,用户应该负责确保列出的扩展在本地和远程服务器上都存在且保持一致。否则,远程查询可能失败或者行为异常。
fetch_size
这个选项指定在每次获取行的操作中postgres_fdw应该得到的行数。可以为一个外部表或者外部服务器指定这个选项。在表上指定的选项将会覆盖在服务器级别上指定的选项。默认值为100。
D.33.1.5. 可更新性选项
默认情况下,所有使用postgres_fdw的外部表都被假定是可更新的。
这可以使用下列选项覆盖:
updatable
这个选项控制postgres_fdw是否允许外部表被使用INSERT、UPDATE和DELETE命令更新。
它可以为一个外部表或一个外部服务器指定。一个表级选项会覆盖一个服务器级选项。
默认值是true。
当然,如果远程表实际上并非可更新的,将产生一个错误。这个选项的使用主要是允许在不查询远程服务器的情况下在本地抛出错误。但是要注意information_schema视图会根据这个选项的设置报告一个postgres_fdw外部表是可更新的(或者不可更新),而不需要远程服务器的任何检查。
D.33.1.6. 导入选项
postgres_fdw能使用IMPORT FOREIGN SCHEMA导入外部表定义。这个命令会在本地服务器上创建外部表定义,这个定义能匹配存在于远程服务器上的表或者视图。如果要被导入的远程表有用户自定义数据类型的列,本地服务器上也必须具有相同名称的兼容类型。
导入行为可以用下列选项自定义(在IMPORT FOREIGN SCHEMA命令中给出):
import_collate
这个选项控制是否在从外部服务器导入的外部表定义中包括列的COLLATE选项。默认是true。如果远程服务器具有和本地服务器不同的排序规则名集合,可能需要关闭这个选项,在不同的操作系统上运行时很可能就是这样。
import_default
这个选项控制是否在从外部服务器导入的外部表定义中包括列的DEFAULT表达式。默认是false。如果启用这个选项,要当心在远程服务器和本地服务器上计算表达式的方式不同,nextval()常会导致这类问题。如果导入的默认值表达式使用了一个本地不存在的函数或者操作符,IMPORT将整个失败。
import_not_null
这个选项控制是否在从外部服务器导入的外部表定义中包括列的NOT NULL约束。默认是true。
注意:除NOT NULL之外的约束将不会从远程表中导入。虽然Halo确实支持外部表上的CHECK约束,但不会自动导入它们,因为存在本地和远程服务器计算约束表达式方式不同的风险。CHECK约束中的任何这类不一致都可能导致查询优化中很难检测的错误。如果你希望导入CHECK约束,你必须手工来做,并且你应该仔细地验证每一个这种约束的语义。
自动排除作为其他表的分区的表或外部表。分区表被导入,除非它们是其他表的分区。由于所有数据都可以通过作为分区层次根的分区表来访问, 所以这种方法应该允许访问所有数据而不创建额外的对象。
连接管理
postgres_fdw在第一个使用关联到外部服务器的外部表的查询期间建立一个到外部服务器的连接。这个连接会被保持,并被重用于同一个会话中的后续查询。但是,如果使用了多个用户实体(用户映射)来访问外部服务器,会为每一个用户映射建立一个连接。
事务管理
在一个引用外部服务器上任何远程表的查询期间,如果还没有根据当前的本地事务打开一个远程事务,postgres_fdw将在远程服务器上打开一个事务。当本地事务提交或中止时,远程事务也被提交或中止。保存点也相似地采用创建相应的远程保存点来管理。
当本地事务为SERIALIZABLE隔离级别时,远程事务使用SERIALIZABLE隔离级别;否则它使用REPEA TABLE READ隔离级别。如果一个查询在远程服务器上执行多个表查询,这种选择保证它将为所有扫描得到快照一致的结果。一种后果是在单一事务中的后继查询将会看到来自远程服务器的相同数据,即便由于其他活动在远程服务器上发生了其他并发更新。如果本地事务使用SERIALIZABLE或REPEATABLE READ隔离级别,这种行为也是可以预期的,但是对于一个READ COMMITTED本地事务它是奇怪的。
请注意postgres_fdw当前不支持为两阶段提交准备远程事务。
远程查询优化
postgres_fdw尝试优化远程查询来减少从外部服务器传来的数据量。这可以通过把查询的WHERE子句发送给远程服务器执行来完成,并且可以不检索当前查询不需要的表列。为了降低查询被误执行的风险,除非WHERE子句使用的数据类型、操作符和函数都是内建的或者属于列在该外部服务器的extensions选项中的一个扩展,将不会把WHERE子句发送到远程服务器。这些子句中的操作符和函数也必须是IMMUTABLE。对于UPDATE或者DELETE查询,如果没有不能发送给远程服务器的WHERE子句、没有查询的本地连接、目标表上没有本地的行级BEFORE或AFTER触发器或存储生成的列,并且没有来自视图的CHECK OPTION约束,postgres_fdw会尝试通过将整个查询发送给远程服务器来优化查询的执行。
在UPDATE中,赋值给目标列的表达式只能使用内建数据类型、IMMUTABLE操作符或者IMMUTABLE操作符,这样能降低查询被误执行的风险。
当postgres_fdw碰到同一个外部服务器上的外部表之间的连接时,它会把整个连接发送给外部服务器,除非由于某些原因它认为逐个从每一个表取得行的效率更高或者涉及的表引用属于不同的用户映射。在发送JOIN子句时,它也会采取和上述WHERE子句相同的预防措施。
实际被发送到远程服务器执行的查询可以使用EXPLAIN VERBOSE来检查。
远程查询执行环境
在postgres_fdw开启的远程会话中,search_path参数只被设置为pg_catalog,因此只有内建对象可以在无模式限定时可见。这对于postgres_fdw本身产生的查询来说不是问题,因为它总是会提供这样的限定。不过,这可能会对在远程服务器上通过触发器或者远程表上的规则执行的函数带来灾难。例如,如果一个远程表实际是一个视图,任何在该视图中使用的函数都将被在这个受限的搜索路径中执行。我们推荐在这类函数中用模式限定所有名称,或者为这类函数附着SET search_path选项(见CREATE FUNCTION)来建立它们所期望的搜索路径环境。
postgres_fdw同样为各种参数建立远程会话设置:
• TimeZone设置为UTC
• DateStyle设置为ISO
• IntervalStyle设置为postgres
• extra_float_digits 设置为3
这些不如search_path有那么多问题,但是如果需要也可以使用函数 SET选项来处理。
我们不推荐通过更改这些参数的会话级设置来推翻这种行为,这很可能会导
致postgres_fdw故障。
例子
这里是一个用postgres_fdw创建外部表的例子。首先安装该扩展:
CREATE EXTENSION postgres_fdw;
然后使用CREATE SERVER创建一个外部服务器。在这个例子中我们希望连接到一个位于主机192.83.123.89上并且监听5432端口的Halo服务器。在该远程服务器上要连接的数据库名为foreign_db:
test=# CREATE SERVER foreign_server
test-# FOREIGN DATA WRAPPER postgres_fdw
test-# OPTIONS (host '192.83.123.89', port '5432', dbname 'foreign_db');
CREATE SERVER
需要用CREATE USER MAPPING定义一个用户映射来标识在远程服务器上使用哪个角色:
CREATE USER MAPPING FOR local_user
SERVER foreign_server
OPTIONS (user 'foreign_user', password 'password');
现在就可以使用CREATE FOREIGN TABLE创建外部表了。在这个例子中我们希望访问远程服务器上名为some_schema.some_table的表。它的本地名称是foreign_table:
test=# CREATE FOREIGN TABLE foreign_table (
test(# id integer NOT NULL,
test(# data text
test(# )
test-# SERVER foreign_server
test-# OPTIONS (schema_name 'some_schema', table_name 'some_table');
CREATE FOREIGN TABLE中声明的列数据类型和其他性质必须要匹配实际的远程表。列名也必须匹配,不过也可以别列附上column_name选项,以表示它们在远程服务器上对应哪个列。在很多情况中,要手工构造外部表定义,使用IMPORT FOREIGN SCHEMA会更好。