Postgresql 中文操作指南

F.38. postgres_fdw — access data stored in external PostgreSQL servers #

postgres_fdw 模块提供外部数据封装 postgres_fdw,它可以用于访问存储在外部 PostgreSQL 服务器中的数据。

此模块提供 的功能与旧的 dblink模块的功能有很大重叠。但是,_postgres_fdw_为访问远程表格提供了更透明和符合标准的语法,在很多情况下能提供更好的性能。

为使用 postgres_fdw 准备远程访问:

现在,您只需 SELECT 从外部表即可访问存储在它下层的远程表中的数据。您还可以使用 INSERTUPDATEDELETECOPYTRUNCATE 修改远程表。(当然,您在用户映射中指定的远程用户必须具有执行这些操作的特权。)

请注意,在访问或修改远程表时,SELECTUPDATEDELETETRUNCATE 中指定的 ONLY 选项无效。

请注意,postgres_fdw 当前不支持带 ON CONFLICT DO UPDATE 子句的 INSERT 语句。但是,ON CONFLICT DO NOTHING 子句受支持,只要省略唯一的索引推断规范。另请注意,postgres_fdw 支持由对分区表执行的 UPDATE 语句调用的行移动,但它当前不处理在同一命令中远程分区被选择用于将移动的行插入其中而该分区又将作为 UPDATE 目标分区而在其他地方被更新的情况。

通常建议,根据所引用的远程表的列来使用完全相同的数据类型(如适用,也包括校对)来声明外表的列。虽然 postgres_fdw 在按需执行数据类型转换方面当前相当宽容,但由于远程服务器对查询条件的解释与本地服务器不同,当类型或校对不匹配时可能会出现令人惊讶的语义异常。

请注意,与底层远程表相比,外表的列可以更少,或列顺序不同。与远程表的列匹配是根据名称,而不是位置。

F.38.1. FDW Options of postgres_fdw #

F.38.1.1. Connection Options #

使用 _postgres_fdw_外部数据包装器的外部服务器可以与 libpq 接受的连接字符串中的选项相同,如 Section 34.1.2中所述,但这些选项不被允许或有特殊处理:

只有超级用户才能使用 sslcertsslkey 设置创建或修改用户映射。

非超级用户可以使用密码身份验证或 GSSAPI 委派凭证连接到外来服务器,因此请为非超级用户的用户映射指定 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。请记住,映射的用户可能能够使用 unix 主目录中 postgres 服务器运行的系统用户中的任何客户端证书、.pgpass.pg_service.conf 等。他们还可以使用诸如 peerident 身份验证等身份验证模式授予的任何信任关系。

F.38.1.2. Object Name Options #

这些选项可用于控制发送到远程 PostgreSQL 服务器的 SQL 语句中使用的名称。当以不同于底层远程表的名称创建外表时,需要这些选项。

  • schema_name (string)

    • 该选项可以针对外部表指定,它提供外部表在远程服务器上要使用的模式名称。如果省略此选项,则使用外部表架构的名称。

  • table_name (string)

    • 该选项可以针对外部表指定,它提供外部表在远程服务器上要使用的表名称。如果省略此选项,则使用外部表的名称。

  • column_name (string)

    • 此选项可为外部表的列指定,给出了用于远程服务器列的列名。如果省略此选项,则使用列的名称。

F.38.1.3. Cost Estimation Options #

postgres_fdw 通过对远程服务器执行查询以检索远程数据,因此理想情况下,扫描外表估计的成本应该是远程服务器执行所需的所有成本外加某些通信开销。获取此类估算的最可靠方法是询问远程服务器,然后为开销添加某些内容 — 但对于简单查询,可能不值得进行额外的远程查询以获取成本估算。因此 postgres_fdw 提供以下选项以控制如何执行成本估算:

  • use_remote_estimate (boolean)

    • 此选项可用于外表或外来服务器中,用来控制 postgres_fdw 是否发出远程 EXPLAIN 命令来获取成本估算。外表中的设置将覆盖其服务器的任何设置,但仅针对该表。默认值为 false

  • fdw_startup_cost (floating point)

    • 此选项可用于外来服务器中,是添加至该服务器上任何外表扫描的估计启动成本的浮点数。它表示建立连接、在远程端解析和规划查询等的额外开销。默认值为 100

  • fdw_tuple_cost (floating point)

    • 此选项可用于外来服务器中,是用于该服务器上的外表扫描的单位元组额外成本的浮点数。它表示服务器之间数据传输的额外开销。您也许可以增加或减少该数字,以反映到远程服务器的网络延迟较高或较低。默认值为 0.01

use_remote_estimate 为 true 时, postgres_fdw 从远程服务器获取行计数和成本估算,然后再将 fdw_startup_costfdw_tuple_cost 添加到成本估算中。当 use_remote_estimate 为否时, postgres_fdw 执行本地行计数和成本估算,然后再将 fdw_startup_costfdw_tuple_cost 添加到成本估算中。除非有远程表的统计信息本地副本,否则本地估算不太可能非常精确。在外部表上运行 ANALYZE 是更新本地统计数据的方式;此操作会扫描远程表,然后计算并存储统计信息,就好像该表是本地表一样。保留本地统计数据是减少远程表的每次查询规划开销的有用方式——但如果经常更新远程表,则本地统计数据很快就会过时。

以下选项控制此类 ANALYZE 操作的行为:

  • analyze_sampling (string)

    • 此选项可为外部表或外部服务器指定,确定 ANALYZE 外部表中的命令 ANALYZE 是否在远程一侧对数据进行采样或读取并传输所有数据并在本地执行采样。支持的值有 offrandomsystembernoulliautooff 禁用来进行远程采样,因此所有数据都将在本地传输和采样。random 使用 random() 函数执行远程采样以选择要返回的行,而 systembernoulli 则依赖于这些名称的内置 TABLESAMPLE 方法。random 在所有远程服务器版本上都能使用,而 TABLESAMPLE 仅从 9.5 版开始支持。auto(默认值)会自动选择推荐的采样方法;当前意味着 bernoullirandom,具体取决于远程服务器版本。

F.38.1.4. Remote Execution Options #

默认情况下,只考虑使用内置运算符和函数的 WHERE 子句在远程服务器上执行。涉及非内置函数的子句在获取行后在本地进行检查。如果远程服务器上提供了这些函数并且可以依靠它们生成与在本地相同的结果,那么发送这种 WHERE 子句以远程执行可以提高性能。此行为可以使用以下选项进行控制:

  • extensions (string)

    • 此选项是一个逗号分隔的 PostgreSQL 扩展名列表,其以兼容版本安装在本地服务器和远程服务器上。属于列出扩展名的不可变函数和运算符将被认为可以运送至远程服务器。此选项只能为外部服务器指定,不能为每个表指定。

    • 使用 extensions 选项时, it is the user’s responsibility 使得列出的扩展存在,并且在本地和远程服务器上行为一致。否则,远程查询可能会失败或行为异常。

  • fetch_size (integer)

    • 此选项指定 postgres_fdw 应该在每次获取操作中获取的行数。可为外部表或外部服务器指定此选项。表上指定的选项会覆盖为服务器指定的选项。默认值是 100

  • batch_size (integer)

    • 此选项指定 postgres_fdw 应该在每次插入操作中插入的行数。可为外部表或外部服务器指定此选项。表上指定的选项会覆盖为服务器指定的选项。默认值是 1

    • 请注意,postgres_fdw 一次插入的行数实际取决于列数和提供的 batch_size 值。批处理作为单个查询执行,libpq 协议(postgres_fdw 用来连接至远程服务器)将单个查询中的参数数量限制为 65535。当列数 * batch_size 超过此限制时,batch_size 会进行调整以避免错误。

    • 在复制到外部表时,此选项也适用。在这种情况下,postgres_fdw 一次复制的行数实际确定方式与插入情况类似,但由于 COPY 命令的实施限制,将始终限制为不超过 1000 行。

F.38.1.5. Asynchronous Execution Options #

postgres_fdw 支持异步执行,它并发运行 Append 节点的多个部分,而不是串行运行以提高性能。此执行可以使用以下选项进行控制:

  • async_capable (boolean)

    • 此选项控制 postgres_fdw 是否允许外部表为异步执行进行并发扫描。可为外部表或外部服务器指定此选项。表级选项会覆盖服务器级选项。默认值是 false

    • 为确保从外部服务器返回的数据保持一致性,postgres_fdw 仅针对指定的外部服务器打开一个连接,且针对该服务器按顺序运行所有查询,即使涉及到多个外部表,除非这些表受到不同的用户映射的约束。在这种情况下,禁用此选项以消除与异步运行查询相关的开销可能使性能得到更高的提升。

    • 即使 Append 节点包含以同步方式执行的子计划和以异步方式执行的子计划,也会应用异步执行。在此情况下,如果要异步处理的子计划使用 postgres_fdw 进行处理,则异步子计划产生的元组只有在至少有一个同步子计划返回所有元组之后才会返回,因为该子计划在异步子计划等待外部服务器返回的异步查询结果时执行。这种行为可能会在未来版本中发生改变。

F.38.1.6. Transaction Management Options #

如事务管理部分中所述,在 postgres_fdw 中,事务通过创建相应的远程事务来管理,而子事务通过创建相应的远程子事务来管理。当涉及到多个远程事务时,默认 postgres_fdw 在提交或终止本地事务时按顺序提交或终止这些远程事务。当涉及到多个远程子事务时,默认 postgres_fdw 在提交或终止本地子事务时按顺序提交或终止这些远程子事务。可以使用以下选项提高性能:

  • parallel_commit (boolean)

    • 此选项控制 postgres_fdw 在提交本地事务时是否并行提交外部服务器上在本地事务中打开的远程事务。此设置也适用于远程和本地子事务。此选项只能为外部服务器指定,不能为每个表指定。默认值是 false

  • parallel_abort (boolean)

    • 此选项控制 postgres_fdw 在终止本地事务时是否并行终止外部服务器上在本地事务中打开的远程事务。此设置也适用于远程和本地子事务。此选项只能为外部服务器指定,不能为每个表指定。默认值是 false

如果涉及到启用此类选项的多个外部服务器于本地事务中,那么当提交或终止本地事务时,这些外部服务器上多个远程事务将并行在这些外部服务器上提交或终止。

启用这些选项时,本地事务提交或终止时,带有大量远程事务的外部服务器可能会看到负面性能影响。

F.38.1.7. Updatability Options #

默认情况下,所有使用 postgres_fdw 的外部服务器都被视为可更新的。可以使用以下选项覆盖此设置:

  • updatable (boolean)

    • 此选项控制 postgres_fdw 是否允许使用 INSERTUPDATEDELETE 命令修改外部表。可为外部表或外部服务器指定此选项。表级选项会覆盖服务器级选项。默认值是 true

    • 当然,如果远程表实际上不可更新,无论如何都将发生错误。使用此选项主要允许在不查询远程服务器的情况下在本地引发错误。然而,请注意,information_schema 视图将报告 postgres_fdw 外表可更新(或不可更新),因为是根据此选项的设置,而无需检查远程服务器。

F.38.1.8. Truncatability Options #

默认情况下,使用 postgres_fdw 的所有外键表都假定为可截断的。可以使用以下选项覆盖此操作:

  • truncatable (boolean)

    • 此选项控制 postgres_fdw 是否允许使用 TRUNCATE 命令来截断外键表。可以为外键表或外键服务器指定此选项。表级选项会覆盖服务器级选项。默认值为 true

    • 当然,如果远程表实际上不可截断,无论如何都会发生错误。使用此选项主要允许在不查询远程服务器的情况下在本地引发错误。

F.38.1.9. Importing Options #

postgres_fdw 能够使用 IMPORT FOREIGN SCHEMA 导入外部表定义。此命令在本地服务器上创建外部表定义,该定义与远程服务器上存在的表或视图相匹配。如果要导入的远程表有用户自定义数据类型的列,那么本地服务器必须有相同名称的兼容类型。

可以使用以下选项(在 IMPORT FOREIGN SCHEMA 命令中给出)自定义导入行为:

  • import_collate (boolean)

    • 此选项控制是否将列 COLLATE 选项包括在从外部服务器导入的外部表的定义中。默认值为 true 。如果远程服务器具有与本地服务器不同的排序名称集,你可能需要将其关闭,如果它在不同的操作系统上运行,则很可能出现这种情况。但是,如果你这样做,则所导入表列的排序很有可能与底层数据不匹配,从而导致异常查询行为。

    • 即使将此参数设置为 true ,导入排序为远程服务器默认值的列也可能存在风险。它们将使用 COLLATE "default" 导入,它将选择本地服务器的默认排序,而本地服务器的默认排序可能不同。

  • import_default (boolean)

    • 此选项控制是否将列 DEFAULT 表达式包含在从外键服务器导入的外键表的定义中。默认值为 false。如果你启用此选项,请小心本地服务器上可能与远程服务器不同的计算方式的默认值;nextval() 是常见的问题来源。如果导入的默认表达式使用本地不存在的函数或运算符,则 IMPORT 将完全失败。

  • import_generated (boolean)

    • 此选项控制是否将列 GENERATED 表达式包含在从外键服务器导入的外键表的定义中。默认值为 true。如果导入的生成表达式使用本地不存在的函数或运算符,则 IMPORT 将完全失败。

  • import_not_null (boolean)

    • 此选项控制是否将列 NOT NULL 约束包含在从外键服务器导入的外键表的定义中。默认值为 true

请注意,除了 NOT NULL 以外的约束绝不会从远程表中导入。尽管 PostgreSQL 确实支持外部表上的检查约束,但没有规定自动导入这些约束,因为存在约束表达式在本地和远程服务器上评估不同的风险。检查约束的行为中的任何此类不一致性都可能导致查询优化中难以检测到的错误。因此,如果你希望导入检查约束,则必须手动执行此操作,并且应仔细验证每个约束的语义。有关外部表检查约束处理的更多详细信息,请参阅 CREATE FOREIGN TABLE

只有当在 LIMIT TO 从句中明确指定表或外部表时,才会导入作为某个其他表的分区表或外部表。否则,它们会自动从 IMPORT FOREIGN SCHEMA 中排除。由于可以通过作为分区等级根的分区表访问所有数据,因此只导入分区表应允许访问所有数据而不创建额外对象。

F.38.1.10. Connection Management Options #

默认情况下,postgres_fdw 建立到外键服务器的所有连接都将保持在本地会话中打开以供重新使用。

  • keep_connections (boolean)

    • 此选项控制 postgres_fdw 是否保持与外键服务器的连接处于打开状态,以便后续查询可以重新使用它们。它只能为外键服务器指定。默认值为 on。如果设置为 off,则在此事务结束时将放弃与该外键服务器的所有连接。

F.38.2. Functions #

  • postgres_fdw_get_connections(OUT server_name text, OUT valid boolean) returns setof record

    • 此函数返回 postgres_fdw 从本地会话建立到外键服务器的所有打开连接的外键服务器名称。它还返回每个连接是否有效。如果在当前本地事务中使用了外键服务器连接,但其外键服务器或用户映射被更改或删除(注意,如果服务器已删除,则无效连接的服务器名称将为 NULL),则将返回 false,然后将在该事务结束时关闭此类无效连接。否则将返回 true。如果没有打开的连接,则不会返回任何记录。该函数的示例用法:

postgres=# SELECT * FROM postgres_fdw_get_connections() ORDER BY 1;
 server_name | valid
-------------+-------
 loopback1   | t
 loopback2   | f
  • postgres_fdw_disconnect(server_name text) returns boolean

    • 此函数取消了 postgres_fdw 从本地会话建立到具有给定名称的外键服务器的打开连接。请注意,可以有多个连接使用不同的用户映射连接到给定的服务器。如果在当前本地事务中使用了这些连接,则它们不会断开,并且会报告警告消息。如果断开至少一个连接,此函数将返回 true,否则返回 false。如果没有找到具有给定名称的外键服务器,则会报告错误。该函数的示例用法:

postgres=# SELECT postgres_fdw_disconnect('loopback1');
 postgres_fdw_disconnect
-------------------------
 t
  • postgres_fdw_disconnect_all() returns boolean

    • 此函数取消了 postgres_fdw 从本地会话建立到外键服务器的所有打开连接。如果在当前本地事务中使用了这些连接,则它们不会断开,并且会报告警告消息。如果断开至少一个连接,此函数将返回 true,否则返回 false。该函数的示例用法:

postgres=# SELECT postgres_fdw_disconnect_all();
 postgres_fdw_disconnect_all
-----------------------------
 t

F.38.3. Connection Management #

postgres_fdw 在使用与外键服务器关联的外键表的第一个查询期间建立与外键服务器的连接。默认情况下,此连接被保留并在同一会话中后续查询中重新使用。可以使用 keep_connections 选项来控制外键服务器的此行为。如果使用多个用户标识(用户映射)来访问外键服务器,则为每个用户映射建立一个连接。

更改定义或删除外部服务器或用户映射时,关闭相关连接。但请注意,如果在当前本地事务中使用任何连接,将在事务结束前保留它们。将来使用外部表的查询需要时,将重新建立已关闭的连接。

建立到外部服务器的连接后,在本地或对应的远程会话退出之前,默认将保持该连接。要显式断开连接,可以禁用外部服务器的 keep_connections 选项,或使用 postgres_fdw_disconnectpostgres_fdw_disconnect_all 函数。例如,这些函数可用于关闭不再需要的连接,从而释放外部服务器上的连接。

F.38.4. Transaction Management #

在引用外部服务器上任一远程表的查询期间,如果当前本地事务未对应打开一个远程事务,postgres_fdw 将在远程服务器上打开一个事务。当本地事务提交或中止时,远程事务将提交或中止。保存点通过创建对应的远程保存点进行类似管理。

当本地事务具有 SERIALIZABLE 隔离级别时,远程事务使用 SERIALIZABLE 隔离级别;否则,它使用 REPEATABLE READ 隔离级别。此选择可确保如果查询对远程服务器执行多个表扫描,它将为所有扫描获取快照一致的结果。因此,即使由于其他活动导致远程服务器上发生并发更新,单个事务内的连续查询也将从远程服务器看到相同的数据。如果本地事务使用 SERIALIZABLEREPEATABLE READ 隔离级别,这种行为也是预期的,但对于 READ COMMITTED 本地事务,可能会令人意外。PostgreSQL 未来版本可能会修改这些规则。

请注意,准备远程事务进行两阶段提交目前不受 postgres_fdw 支持。

F.38.5. Remote Query Optimization #

postgres_fdw 尝试优化远程查询以减少从外部服务器传输的数据量。这是通过向远程服务器发送查询 WHERE 子句进行执行,以及不检索当前查询不需要的表列来完成的。为了减少错误执行查询的风险,WHERE 子句不会发送到远程服务器,除非它们仅使用内置数据类型、运算符和函数,或属于外部服务器 extensions 选项中列出的某个扩展程序。此类子句中的运算符和函数也必须 IMMUTABLE。针对 UPDATEDELETE 查询,如果不存在无法发送到远程服务器的查询 WHERE 子句、查询不存在本地连接、对目标表不存在行级本地 BEFOREAFTER 触发器或存储的生成列,以及不存在来自父视图的 CHECK OPTION 约束,postgres_fdw 尝试通过向远程服务器发送整个查询来优化查询执行。在 UPDATE 中,分配给目标列的表达式只能使用内置数据类型、IMMUTABLE 运算符或 IMMUTABLE 函数,以减少错误执行查询的风险。

postgres_fdw 在同一外部服务器上遇到外部表之间的连接时,它将发送整个连接到外部服务器,除非出于某种原因它认为分别从每个表中获取行更有效,或涉及的表引用受不同用户映射的约束。在发送 JOIN 子句时,它会针对 WHERE 子句采取与上述相同的预防措施。

可以使用 EXPLAIN VERBOSE 检查实际发送到远程服务器以进行执行的查询。

F.38.6. Remote Query Execution Environment #

在由 postgres_fdw 打开的远程会话中, search_path 参数仅设置为 pg_catalog ,以便在没有模式限定的情况下只能看到内置对象。对于由 postgres_fdw 本身生成的查询来说,这不是问题,因为它总是提供此类限定条件。然而,这可能会对通过远程表上的触发器或规则在远程服务器上执行的函数构成威胁。例如,如果远程表实际上是视图,那么在该视图中使用的任何函数都将使用受限的搜索路径执行。建议对此类函数中的所有名称加上模式限定,或者向此类函数附加 SET search_path 选项(请参阅 CREATE FUNCTION )以建立它们预期的搜索路径环境。

postgres_fdw 同样为各种参数建立远程会话设置:

search_path 相比,这些参数不太可能出现问题,但如果需要,可以使用函数 SET 选项处理它们。

建议您通过更改这些参数的会话级设置来覆盖此行为;这可能会导致 postgres_fdw 出现故障。

F.38.7. Cross-Version Compatibility #

postgres_fdw 可用于早在 PostgreSQL 8.3 时的远程服务器。可追溯到 8.1 的只读功能。然而,有一个限制,即 postgres_fdw 通常假设不可变的内置函数和运算符可以安全地发送到远程服务器执行,如果它们出现在外部表的 WHERE 子句中。因此,在远程服务器发布后添加的内置函数可能会被发送到远程服务器执行,从而导致“函数不存在”或类似错误。可以通过重写查询来解决此类故障,例如将外部表引用嵌入到子 SELECT 中,并使用 OFFSET 0 作为优化标识,并将有问题的函数或运算符放在子 SELECT 之外。

F.38.8. Configuration Parameters #

  • postgres_fdw.application_name (string) #

    • 指定在 postgres_fdw 建立与外部服务器的连接时使用的 application_name 配置参数的值。这会覆盖服务器对象的 application_name 选项。请注意,此参数的更改会影响现有的连接,直到重新建立它们。

    • postgres_fdw.application_name 可以是任何长度的任意字符串,甚至包含非 ASCII 字符。但是当将其传递给 application_name 并用作外部服务器中的 application_name 时,请注意,它将被截断为少于 NAMEDATALEN 个字符。除可打印 ASCII 字符以外的任何字符都将替换为 C-style hexadecimal escapes 。有关详细信息,请参阅 application_name

    • % 字符开始“转义序列”,这些序列将替换为如下所述的状态信息。将忽略无法识别的转义符。其他字符将直接复制到应用程序名称。请注意,它不允许在 % 之后和选项之前指定加/减号或数字文字,以用于对齐和填充。

    • 例如,假设用户 local_user 从数据库 local_db 建立到 foreign_db(作为用户 foreign_user)的连接,则设置 'db=%d, user=%u' 将替换为 'db=local_db, user=local_user'

F.38.9. Examples #

以下是在 postgres_fdw 中创建外部表的示例。首先安装扩展程序:

CREATE EXTENSION postgres_fdw;

然后使用 CREATE SERVER 创建外部服务器。在此示例中,我们要连接到监听端口 5432 上的主机 192.83.123.89 上的 PostgreSQL 服务器。在远程服务器上,建立连接时会使用数据库 foreign_db 的名称:

CREATE SERVER foreign_server
        FOREIGN DATA WRAPPER postgres_fdw
        OPTIONS (host '192.83.123.89', port '5432', dbname 'foreign_db');

还需要使用 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

CREATE FOREIGN TABLE foreign_table (
        id integer NOT NULL,
        data text
)
        SERVER foreign_server
        OPTIONS (schema_name 'some_schema', table_name 'some_table');

CREATE FOREIGN TABLE 中声明的列的数据类型和其他属性必须与实际远程表相匹配。列名也必须相匹配,除非你为各个列附加 column_name 选项以显示它们在远程表中的命名方式。在很多情况下,使用 IMPORT FOREIGN SCHEMA 比手动构造外部表定义更好。