postgres_fdw
Remote PostgreSQL
通过外部表访问远程 PostgreSQL 服务器中的表。
当前查看 PostgreSQL 18.6。
说明
通过外部表访问远程 PostgreSQL 服务器中的表。
- 接口类别
- 外部数据包装器
- 处理函数或例程
- postgres_fdw_handler
- 记录的回调
- 36
- 数据源
- 远程 PostgreSQL 服务器
接口与能力边界
矩阵记录此源码构建实际注册的回调。注册表明实现了相应接口钩子;操作是否允许还取决于选项、查询形式、权限和提供方规则。
下方保留同版本完整手册,包括配置、约束和示例。不声称已进行运行时能力测试。
异步执行选项
postgres_fdw 支持异步执行,它可以并发运行 Append 节点的多个部分,而不是串行运行,以提高性能。可以使用以下选项控制这种执行方式:
事务管理选项
如“事务管理”一节所述,在 postgres_fdw 中,事务是通过创建对应的远程事务来管理的,子事务则通过创建对应的远程子事务来管理。当当前本地事务涉及多个远程事务时,默认情况下, postgres_fdw 会在本地事务提交或中止时串行提交或中止这些远程事务。当当前本地子事务涉及多个远程子事务时,默认情况下, postgres_fdw 也会在本地子事务提交或中止时串行提交或中止这些远程子事务。使用以下选项可以改善性能:
如果启用了这些选项的多个外部服务器参与同一个本地事务,那么在本地事务提交或中止时,这些外部服务器上的多个远程事务会跨服务器并行提交或中止。
启用这些选项后,若某个外部服务器涉及很多远程事务,则在本地事务提交或中止时,该外部服务器上的性能可能会受到负面影响。
事务管理
在引用某个外部服务器上任意远程表的查询期间,如果当前本地事务尚未在该远程服务器上打开对应的事务, postgres_fdw 就会在该远程服务器上打开一个事务。本地事务提交或中止时,远程事务也会提交或中止。保存点也会通过创建对应的远程保存点进行类似管理。
当本地事务的隔离级别为 SERIALIZABLE 时,远程事务使用 SERIALIZABLE ;否则使用 REPEATABLE READ 隔离级别。这一选择确保如果一个查询在远程服务器上执行多次表扫描,所有扫描都能获得快照一致的结果。其结果是,同一事务中的后续查询会看到来自远程服务器的相同数据,即使远程服务器由于其他活动正在发生并发更新。对于使用 SERIALIZABLE 或 REPEATABLE READ 隔离级别的本地事务,这种行为本来就符合预期;但对于 READ COMMITTED 本地事务,则可能令人意外。未来的 PostgreSQL 版本可能会修改这些规则。
请注意, postgres_fdw 当前不支持为两阶段提交预备远程事务。
远程查询优化
postgres_fdw 会尽力优化远程查询,以减少从外部服务器传输的数据量。这是通过将查询的 WHERE 子句发送到远程服务器执行,以及不获取当前查询不需要的表列来实现的。为降低查询被错误执行的风险,只有当 WHERE 子句使用的所有数据类型、操作符和函数都是内置的,或属于外部服务器 extensions 选项列出的扩展时,才会将该子句发送到远程服务器。这类子句中的操作符和函数还必须是 IMMUTABLE 。对于 UPDATE 或 DELETE 查询, postgres_fdw 会在查询中不存在无法发送到远程服务器的 WHERE 子句、没有本地连接操作、目标表上没有行级本地 BEFORE 或 AFTER 触发器或存储生成列,也没有来自父视图的 CHECK OPTION 约束时,尝试将整个查询发送到远程服务器以优化执行。在 UPDATE 中,为了降低查询被错误执行的风险,赋给目标列的表达式也必须只使用内置数据类型、 IMMUTABLE 操作符或 IMMUTABLE 函数。
当 postgres_fdw 遇到同一外部服务器上的外部表之间的连接时,除非由于某种原因它认为分别从各表取回行会更高效,或者相关表引用受不同用户映射约束,否则会将整个连接发送到远程服务器。在发送 JOIN 子句时,它也会采取与前述 WHERE 子句相同的预防措施。
可以使用 EXPLAIN VERBOSE 查看实际发送给远程服务器执行的查询。
跨版本兼容性
postgres_fdw 可用于访问 PostgreSQL 8.3 及更新的远程服务器;只读能力可追溯到 8.1。
一个限制是,postgres_fdw 通常认为外部表 WHERE 子句中的不可变内置函数和运算符可以安全地发送到远程服务器执行。因此,远程服务器所用版本之后才加入的内置函数也可能被发送过去,导致“function does not exist”或类似错误。可以通过改写查询绕过此问题,例如将外部表引用放入带 OFFSET 0 的子 SELECT 中,以此形成优化屏障,并将有问题的函数或运算符放在子 SELECT 外部。
另一个限制是,在外部表上执行 INSERT 语句并带有 ON CONFLICT DO NOTHING 子句时,远程服务器必须运行 PostgreSQL 9.5 或更高版本,因为更早版本不支持此特性。
核心源码中注册的实现
{
FdwRoutine *routine = makeNode(FdwRoutine);
/* Functions for scanning foreign tables */
routine->GetForeignRelSize = postgresGetForeignRelSize;
routine->GetForeignPaths = postgresGetForeignPaths;
routine->GetForeignPlan = postgresGetForeignPlan;
routine->BeginForeignScan = postgresBeginForeignScan;
routine->IterateForeignScan = postgresIterateForeignScan;
routine->ReScanForeignScan = postgresReScanForeignScan;
routine->EndForeignScan = postgresEndForeignScan;
/* Functions for updating foreign tables */
routine->AddForeignUpdateTargets = postgresAddForeignUpdateTargets;
routine->PlanForeignModify = postgresPlanForeignModify;
routine->BeginForeignModify = postgresBeginForeignModify;
routine->ExecForeignInsert = postgresExecForeignInsert;
routine->ExecForeignBatchInsert = postgresExecForeignBatchInsert;
routine->GetForeignModifyBatchSize = postgresGetForeignModifyBatchSize;
routine->ExecForeignUpdate = postgresExecForeignUpdate;
routine->ExecForeignDelete = postgresExecForeignDelete;
routine->EndForeignModify = postgresEndForeignModify;
routine->BeginForeignInsert = postgresBeginForeignInsert;
routine->EndForeignInsert = postgresEndForeignInsert;
routine->IsForeignRelUpdatable = postgresIsForeignRelUpdatable;
routine->PlanDirectModify = postgresPlanDirectModify;
routine->BeginDirectModify = postgresBeginDirectModify;
routine->IterateDirectModify = postgresIterateDirectModify;
routine->EndDirectModify = postgresEndDirectModify;
/* Function for EvalPlanQual rechecks */
routine->RecheckForeignScan = postgresRecheckForeignScan;
/* Support functions for EXPLAIN */
routine->ExplainForeignScan = postgresExplainForeignScan;
routine->ExplainForeignModify = postgresExplainForeignModify;
routine->ExplainDirectModify = postgresExplainDirectModify;
/* Support function for TRUNCATE */
routine->ExecForeignTruncate = postgresExecForeignTruncate;
/* Support functions for ANALYZE */
routine->AnalyzeForeignTable = postgresAnalyzeForeignTable;
/* Support functions for IMPORT FOREIGN SCHEMA */
routine->ImportForeignSchema = postgresImportForeignSchema;
/* Support functions for join push-down */
routine->GetForeignJoinPaths = postgresGetForeignJoinPaths;
/* Support functions for upper relation push-down */
routine->GetForeignUpperPaths = postgresGetForeignUpperPaths;
/* Support functions for asynchronous execution */
routine->IsForeignPathAsyncCapable = postgresIsForeignPathAsyncCapable;
routine->ForeignAsyncRequest = postgresForeignAsyncRequest;
routine->ForeignAsyncConfigureWait = postgresForeignAsyncConfigureWait;
routine->ForeignAsyncNotify = postgresForeignAsyncNotify;
PG_RETURN_POINTER(routine);
}注册的接口处理函数
| 接口操作 | 源码观察 | 回调 | 实现 |
|---|---|---|---|
| 扫描数据行 | 已注册处理函数;适用条件限制 | IterateForeignScan | postgresIterateForeignScan |
| INSERT | 已注册处理函数;适用条件限制 | ExecForeignInsert | postgresExecForeignInsert |
| UPDATE | 已注册处理函数;适用条件限制 | ExecForeignUpdate | postgresExecForeignUpdate |
| DELETE | 已注册处理函数;适用条件限制 | ExecForeignDelete | postgresExecForeignDelete |
| TRUNCATE | 已注册处理函数;适用条件限制 | ExecForeignTruncate | postgresExecForeignTruncate |
| 连接路径下推 | 已注册处理函数;适用条件限制 | GetForeignJoinPaths | postgresGetForeignJoinPaths |
| 上层路径,包括聚合 | 已注册处理函数;适用条件限制 | GetForeignUpperPaths | postgresGetForeignUpperPaths |
| 批量插入 | 已注册处理函数;适用条件限制 | ExecForeignBatchInsert | postgresExecForeignBatchInsert |
| 异步追加执行 | 已注册处理函数;适用条件限制 | ForeignAsyncRequest | postgresForeignAsyncRequest |
| 导入模式 | 已注册处理函数;适用条件限制 | ImportForeignSchema | postgresImportForeignSchema |
手册中的选项
| 选项 | 同版本定义 |
|---|---|
| schema_name | 该选项可为外部表指定,用于给出在远程服务器上为该外部表使用的模式名。如果省略,则使用外部表自身所在模式的名称。 |
| table_name | 该选项可为外部表指定,用于给出在远程服务器上为该外部表使用的表名。如果省略,则使用外部表自身的名称。 |
| column_name | 该选项可为外部表的某个列指定,用于给出在远程服务器上为该列使用的列名。如果省略,则使用该列自身的名称。 |
| use_remote_estimate | 该选项可为外部表或外部服务器指定,用于控制 postgres_fdw 是否发出远程 EXPLAIN 命令来获取代价估算。外部表上的设置会覆盖其所属服务器的设置,但只对该表生效。默认值为 false 。 |
| fdw_startup_cost | 该选项可为外部服务器指定,是一个浮点值,会被加到该服务器上任何外部表扫描的估计启动代价中。它表示建立连接、在远程端解析并规划查询等额外开销。默认值为 100 。 |
| fdw_tuple_cost | 该选项可为外部服务器指定,是一个浮点值,用作该服务器上外部表扫描的每个元组的额外代价。它表示服务器之间数据传输的额外开销。可以增大或减小该数值,以反映到远程服务器更高或更低的网络延迟。默认值为 0.2 。 |
| analyze_sampling | 此选项可在外部表或外部服务器上设置,决定外部表 ANALYZE 是在远程采样,还是读取并传输全部数据后在本地采样。支持 off、random、system、bernoulli 和 auto。off 禁用远程采样,全部数据会传到本地后采样。random 使用 random() 函数在远程选择返回行;system 和 bernoulli 使用同名内置 TABLESAMPLE 方法。random 适用于所有远程服务器版本,TABLESAMPLE 仅从 9.5 起支持。默认值 auto 自动选择推荐方法,目前根据远程服务器版本选择 bernoulli 或 random。 |
| extensions | 该选项是一个以逗号分隔的 PostgreSQL 扩展名称列表,这些扩展必须在本地和远程服务器上都已安装且版本兼容。属于列出扩展且不可变的函数和操作符,将被视为可下推到远程服务器执行。该选项只能为外部服务器指定,不能按表指定。 使用 extensions 选项时, 确保所列扩展在本地和远程服务器上都存在且行为完全一致,属于用户自己的责任 。否则,远程查询可能失败或出现意外行为。 |
| fetch_size | 该选项指定 postgres_fdw 在每次取回操作中应获取的行数。它可为外部表或外部服务器指定。表上指定的选项会覆盖服务器上指定的选项。默认值为 100 。 |
| batch_size | 此选项指定 postgres_fdw 每次插入操作应插入的行数,可在外部表或外部服务器上设置,表级设置覆盖服务器级设置,默认值为 1。实际一次插入的行数取决于列数和 batch_size。一个批次作为一条查询执行,而 postgres_fdw 用于连接远程服务器的 libpq 协议将单条查询的参数数限制为 65535。当列数乘以 batch_size 超出限制时,会调整 batch_size 以避免报错。此选项也适用于向外部表执行 COPY,实际一次复制的行数按类似方式确定,但受 COPY 实现限制,最多为 1000 行。 |
| async_capable | 该选项控制 postgres_fdw 是否允许为异步执行而并发扫描外部表。它可为外部表或外部服务器指定。表级选项会覆盖服务器级选项。默认值为 false 。 为了确保从外部服务器返回的数据保持一致,除非这些表使用不同的用户映射,否则对于同一外部服务器, postgres_fdw 只会打开一个连接,并且即使涉及多个外部表,也会顺序运行针对该服务器的所有查询。在这种情况下,禁用此选项以消除异步运行查询带来的开销,反而可能具有更好的性能。 即使 Append 节点同时包含同步执行和异步执行的子计划,也会应用异步执行。在这种情况下,如果异步子计划是使用 postgres_fdw 处理的,则在至少有一个同步子计划返回全部元组之前,不会返回异步子计划的元组,因为同步子计划执行时,异步子计划仍在等待发送给外部服务器的异步查询结果。这种行为在未来版本中可能会改变。 |
| parallel_commit | 该选项控制在本地事务提交时, postgres_fdw 是否并行提交该本地事务中在某个外部服务器上打开的远程事务。此设置也适用于远程子事务和本地子事务。该选项只能为外部服务器指定,不能按表指定。默认值为 false 。 |
| parallel_abort | 该选项控制在本地事务中止时, postgres_fdw 是否并行中止该本地事务中在某个外部服务器上打开的远程事务。此设置也适用于远程子事务和本地子事务。该选项只能为外部服务器指定,不能按表指定。默认值为 false 。 |
| updatable | 该选项控制 postgres_fdw 是否允许使用 INSERT 、 UPDATE 和 DELETE 命令修改外部表。它可为外部表或外部服务器指定。表级选项会覆盖服务器级选项。默认值为 true 。 当然,如果远程表实际上不可更新,最终仍会报错。该选项的主要作用是允许在本地直接抛出错误,而无需查询远程服务器。但请注意, information_schema 视图会根据该选项的设置,将 postgres_fdw 外部表报告为可更新(或不可更新),而不会对远程服务器进行任何检查。 |
| truncatable | 该选项控制 postgres_fdw 是否允许使用 TRUNCATE 命令截断外部表。它可为外部表或外部服务器指定。表级选项会覆盖服务器级选项。默认值为 true 。 当然,如果远程表实际上不可截断,最终仍会报错。该选项的主要作用是允许在本地直接抛出错误,而无需查询远程服务器。 |
| import_collate | 该选项控制从外部服务器导入的外部表定义中是否包含列的 COLLATE 选项。默认值为 true 。如果远程服务器的排序规则名称集合与本地服务器不同,则可能需要关闭此选项;如果远程服务器运行在不同操作系统上,这种情况尤其可能发生。不过,如果这样做,导入表列的排序规则就存在与底层数据不匹配的严重风险,从而导致查询行为异常。 即使将此参数设置为 true ,导入排序规则为远程服务器默认值的列仍可能有风险。这些列会以 COLLATE "default" 导入,这将选择本地服务器的默认排序规则,而它可能并不相同。 |
| import_default | 该选项控制从外部服务器导入的外部表定义中是否包含列的 DEFAULT 表达式。默认值为 false 。如果启用此选项,需要警惕那些在本地服务器上的计算结果可能与远程服务器不同的默认值; nextval() 是常见的问题来源。如果导入的默认值表达式使用了本地不存在的函数或操作符,则整个 IMPORT 将失败。 |
| import_generated | 该选项控制从外部服务器导入的外部表定义中是否包含列的 GENERATED 表达式。默认值为 true 。如果导入的生成表达式使用了本地不存在的函数或操作符,则整个 IMPORT 将失败。 |
| import_not_null | 该选项控制从外部服务器导入的外部表定义中是否包含列的 NOT NULL 约束。默认值为 true 。 |
| keep_connections | 该选项控制 postgres_fdw 是否保持与外部服务器的连接处于打开状态,以便后续查询重用它们。该选项只能为外部服务器指定。默认值为 on 。如果设置为 off ,则到该外部服务器的所有连接都会在每个事务结束时被丢弃。 |
| use_scram_passthrough | 该选项控制 postgres_fdw 在连接到外部服务器时是否使用 SCRAM 透传认证。该选项可为外部服务器或用户映射指定。用户映射的设置会覆盖外部服务器的设置。使用 SCRAM 透传认证时, postgres_fdw 使用经 SCRAM Hash 处理的凭据,而不是明文用户密码连接远程服务器。这样可以避免在 PostgreSQL 系统目录中存储明文用户密码。 要使用 SCRAM 透传认证: 远程服务器必须请求 scram-sha-256 认证方法;否则连接将失败。 远程服务器可以是任何支持 SCRAM 的 PostgreSQL 版本。对 use_scram_passthrough 的支持只要求客户端一侧(FDW 侧)具备。 用户映射密码不会被使用。 运行 postgres_fdw 的服务器与远程服务器,必须针对用于在 postgres_fdw 上认证到外部服务器的该用户,拥有完全相同的 SCRAM 凭据(加密密码)(盐值和迭代次数都必须相同,而不仅仅是密码相同)。 因而,如果要建立到多个主机的 FDW 连接,例如用于分区外部表或分片,则所有主机都必须为相关用户保存完全相同的 SCRAM 凭据。 发起对外 FDW 连接的 PostgreSQL 实例中,当前会话的传入客户端连接也必须使用 SCRAM 认证。(因此称为 “ 透传 ” :SCRAM 必须在进入和离开时都被使用。)这是 SCRAM 协议的技术要求。 |
手册定义
F.38. postgres_fdw — 访问存储在外部 PostgreSQL 服务器中的数据
F.38. postgres_fdw — 访问存储在外部 PostgreSQL 服务器中的数据
postgres_fdw 模块提供外部数据包装器 postgres_fdw,可用于访问存储在外部 PostgreSQL 服务器中的数据。
本模块提供的功能与较旧的dblink模块在很大程度上重叠。但 postgres_fdw 为访问远程表提供了更透明且符合标准的语法,并且在许多情况下性能更好。
要准备通过 postgres_fdw 进行远程访问:
-
安装
postgres_fdw扩展,可使用CREATE EXTENSION。 -
使用CREATE SERVER创建外部服务器对象,用来表示每个要连接的远程数据库。将除
user和password之外的连接信息指定为服务器对象的选项。 -
对于每个需要获准访问各个外部服务器的数据库用户,使用CREATE USER MAPPING创建用户映射。将要使用的远程用户名和密码指定为用户映射的
user和password选项。 -
对于每个要访问的远程表,使用CREATE FOREIGN TABLE或IMPORT FOREIGN SCHEMA创建外部表。外部表的列必须与被引用的远程表匹配。不过,如果在外部表对象的选项中指定正确的远程名称,也可以使用与远程表不同的表名和/或列名。
现在,只需对外部表执行 SELECT,即可访问其底层远程表中存储的数据。也可以使用 INSERT、UPDATE、DELETE、COPY 或 TRUNCATE 修改远程表。(当然,在用户映射中指定的远程用户必须拥有执行这些操作的权限。)
请注意,在访问或修改远程表时,ONLY 选项在 SELECT、UPDATE、DELETE 或 TRUNCATE 中不起作用。
postgres_fdw 目前不支持带 ON CONFLICT DO UPDATE 子句的 INSERT。不过,支持 ON CONFLICT DO NOTHING,前提是不指定唯一索引推断条件。postgres_fdw 还支持在分区表上执行 UPDATE 所触发的行移动,但目前不支持以下情况:用于接收移入行的远端分区,同时也是同一命令中其他位置将要更新的 UPDATE 目标分区。
通常建议将外部表的列声明为与被引用远程表的对应列具有完全相同的数据类型,并在适用时具有相同的排序规则。尽管 postgres_fdw 目前在按需执行数据类型转换方面相当宽容,但当类型或排序规则不匹配时,仍可能出现令人意外的语义异常,因为远程服务器对查询条件的解释可能与本地服务器不同。
请注意,外部表的声明可以比其底层远程表少一些列,或者使用不同的列顺序。与远程表列的匹配是按名称而不是按位置进行的。
F.38.1.1. 连接选项
F.38.1.1. 连接选项
使用 postgres_fdw 外部数据包装器的外部服务器,可以使用 libpq 在连接字符串中接受的相同选项,详见第 32.1.2 节;但以下选项不允许使用,或会被特殊处理:
-
user、password和sslpassword(请改为在用户映射中指定,或使用服务文件) -
client_encoding(会根据本地服务器编码自动设置) -
application_name可以出现在连接选项和postgres_fdw.application_name中的任一处,或同时出现在二者中。如果两者都存在,postgres_fdw.application_name会覆盖连接设置。与 libpq 不同,postgres_fdw允许application_name包含“转义序列”。详见postgres_fdw.application_name。 -
fallback_application_name(始终设置为postgres_fdw) -
sslkey和sslcert可以出现在连接选项、用户映射中的任一处,或同时出现在二者中。如果两者都存在,用户映射设置会覆盖连接设置。
只有超级用户才能创建或修改带有 sslcert 或 sslkey 设置的用户映射。
非超级用户可以通过密码认证或使用 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。请注意,映射用户可能使用运行 postgres 服务器的系统用户的 Unix 主目录中的任何客户端证书、.pgpass、.pg_service.conf 等。(查找主目录的规则详见第 32.16 节。)他们也可能利用 peer、ident 等认证方式授予的任何信任关系。
F.38.1.2. 对象名称选项
F.38.1.2. 对象名称选项
这些选项可用于控制发送到远程 PostgreSQL 服务器的 SQL 语句中所使用的名称。当创建外部表时所用的名称与其底层远程表的名称不同时,就需要这些选项。
schema_name(string)-
该选项可为外部表指定,用于给出在远程服务器上为该外部表使用的模式名。如果省略,则使用外部表自身所在模式的名称。
table_name(string)-
该选项可为外部表指定,用于给出在远程服务器上为该外部表使用的表名。如果省略,则使用外部表自身的名称。
column_name(string)-
该选项可为外部表的某个列指定,用于给出在远程服务器上为该列使用的列名。如果省略,则使用该列自身的名称。
F.38.1.3. 代价估算选项
F.38.1.3. 代价估算选项
postgres_fdw 通过在远程服务器上执行查询来获取远程数据,因此,理想情况下,扫描外部表的估计代价应当等于在远程服务器上完成该操作的代价,再加上一些通信开销。获得这种估算最可靠的方法,是向远程服务器询问,再把开销加上去;但对于简单查询,为了取得代价估算而额外发送一次远程查询,可能并不划算。因此 postgres_fdw 提供以下选项来控制代价估算的方式:
use_remote_estimate(boolean)-
该选项可为外部表或外部服务器指定,用于控制
postgres_fdw是否发出远程EXPLAIN命令来获取代价估算。外部表上的设置会覆盖其所属服务器的设置,但只对该表生效。默认值为false。 fdw_startup_cost(floating point)-
该选项可为外部服务器指定,是一个浮点值,会被加到该服务器上任何外部表扫描的估计启动代价中。它表示建立连接、在远程端解析并规划查询等额外开销。默认值为
100。 fdw_tuple_cost(floating point)-
该选项可为外部服务器指定,是一个浮点值,用作该服务器上外部表扫描的每个元组的额外代价。它表示服务器之间数据传输的额外开销。可以增大或减小该数值,以反映到远程服务器更高或更低的网络延迟。默认值为
0.2。
当 use_remote_estimate 为真时,postgres_fdw 从远程服务器获取行数和代价估算,然后将 fdw_startup_cost 和 fdw_tuple_cost 加到代价估算中。当 use_remote_estimate 为假时,postgres_fdw 在本地执行行数和代价估算,然后再将 fdw_startup_cost 和 fdw_tuple_cost 加到代价估算中。除非有远程表统计信息的本地副本可用,否则这种本地估算不太可能非常准确。更新本地统计信息的方法,是在外部表上运行ANALYZE;这样会扫描远程表,然后像对待本地表一样计算并存储统计信息。保留本地统计信息可以有效减少远程表每次查询的规划开销;但如果远程表经常更新,本地统计信息很快就会过时。
以下选项控制这种 ANALYZE 操作的行为:
analyze_sampling(string)-
此选项可在外部表或外部服务器上设置,决定外部表 ANALYZE 是在远程采样,还是读取并传输全部数据后在本地采样。支持 off、random、system、bernoulli 和 auto。off 禁用远程采样,全部数据会传到本地后采样。random 使用 random() 函数在远程选择返回行;system 和 bernoulli 使用同名内置 TABLESAMPLE 方法。random 适用于所有远程服务器版本,TABLESAMPLE 仅从 9.5 起支持。默认值 auto 自动选择推荐方法,目前根据远程服务器版本选择 bernoulli 或 random。
F.38.1.4. 远程执行选项
F.38.1.4. 远程执行选项
默认情况下,只有使用内置操作符和函数的 WHERE 子句才会被考虑在远程服务器上执行。涉及非内置函数的子句会在取回行之后在本地检查。如果这些函数在远程服务器上也可用,并且可以确信其结果与本地相同,则将这类 WHERE 子句发送到远程端执行可以提高性能。可以使用以下选项控制此行为:
extensions(string)-
该选项是一个以逗号分隔的 PostgreSQL 扩展名称列表,这些扩展必须在本地和远程服务器上都已安装且版本兼容。属于列出扩展且不可变的函数和操作符,将被视为可下推到远程服务器执行。该选项只能为外部服务器指定,不能按表指定。
使用
extensions选项时,确保所列扩展在本地和远程服务器上都存在且行为完全一致,属于用户自己的责任。否则,远程查询可能失败或出现意外行为。 fetch_size(integer)-
该选项指定
postgres_fdw在每次取回操作中应获取的行数。它可为外部表或外部服务器指定。表上指定的选项会覆盖服务器上指定的选项。默认值为100。 batch_size(integer)-
该选项指定
postgres_fdw在每次插入操作中应插入的行数。它可为外部表或外部服务器指定。表上指定的选项会覆盖服务器上指定的选项。默认值为1。postgres_fdw 每次实际插入的行数取决于列数与指定的 batch_size。一个批次作为单条查询执行,而 postgres_fdw 用于连接远端服务器的 libpq 协议将单条查询的参数数量限制为 65535。当列数 * batch_size 超过此限制时,会调整 batch_size 以避免错误。
该选项也适用于向外部表复制数据。在这种情况下,
postgres_fdw实际一次复制的行数会以与插入场景类似的方式确定,但由于COPY命令的实现限制,最多只能为 1000 行。
F.38.1.5. 异步执行选项
F.38.1.5. 异步执行选项
postgres_fdw 支持异步执行,它可以并发运行 Append 节点的多个部分,而不是串行运行,以提高性能。可以使用以下选项控制这种执行方式:
async_capable(boolean)-
该选项控制
postgres_fdw是否允许为异步执行而并发扫描外部表。它可为外部表或外部服务器指定。表级选项会覆盖服务器级选项。默认值为false。为了确保从外部服务器返回的数据保持一致,除非这些表使用不同的用户映射,否则对于同一外部服务器,
postgres_fdw只会打开一个连接,并且即使涉及多个外部表,也会顺序运行针对该服务器的所有查询。在这种情况下,禁用此选项以消除异步运行查询带来的开销,反而可能具有更好的性能。即使
Append节点同时包含同步执行和异步执行的子计划,也会应用异步执行。在这种情况下,如果异步子计划是使用postgres_fdw处理的,则在至少有一个同步子计划返回全部元组之前,不会返回异步子计划的元组,因为同步子计划执行时,异步子计划仍在等待发送给外部服务器的异步查询结果。这种行为在未来版本中可能会改变。
F.38.1.6. 事务管理选项
F.38.1.6. 事务管理选项
如“事务管理”一节所述,在 postgres_fdw 中,事务是通过创建对应的远程事务来管理的,子事务则通过创建对应的远程子事务来管理。当当前本地事务涉及多个远程事务时,默认情况下,postgres_fdw 会在本地事务提交或中止时串行提交或中止这些远程事务。当当前本地子事务涉及多个远程子事务时,默认情况下,postgres_fdw 也会在本地子事务提交或中止时串行提交或中止这些远程子事务。使用以下选项可以改善性能:
parallel_commit(boolean)-
该选项控制在本地事务提交时,
postgres_fdw是否并行提交该本地事务中在某个外部服务器上打开的远程事务。此设置也适用于远程子事务和本地子事务。该选项只能为外部服务器指定,不能按表指定。默认值为false。 parallel_abort(boolean)-
该选项控制在本地事务中止时,
postgres_fdw是否并行中止该本地事务中在某个外部服务器上打开的远程事务。此设置也适用于远程子事务和本地子事务。该选项只能为外部服务器指定,不能按表指定。默认值为false。
如果启用了这些选项的多个外部服务器参与同一个本地事务,那么在本地事务提交或中止时,这些外部服务器上的多个远程事务会跨服务器并行提交或中止。
启用这些选项后,若某个外部服务器涉及很多远程事务,则在本地事务提交或中止时,该外部服务器上的性能可能会受到负面影响。
F.38.1.7. 可更新性选项
F.38.1.7. 可更新性选项
默认情况下,所有使用 postgres_fdw 的外部表都被假定为可更新。这一点可以通过以下选项覆盖:
updatable(boolean)-
该选项控制
postgres_fdw是否允许使用INSERT、UPDATE和DELETE命令修改外部表。它可为外部表或外部服务器指定。表级选项会覆盖服务器级选项。默认值为true。当然,如果远程表实际上不可更新,最终仍会报错。该选项的主要作用是允许在本地直接抛出错误,而无需查询远程服务器。但请注意,
information_schema视图会根据该选项的设置,将postgres_fdw外部表报告为可更新(或不可更新),而不会对远程服务器进行任何检查。
F.38.1.8. 可截断性选项
F.38.1.8. 可截断性选项
默认情况下,所有使用 postgres_fdw 的外部表都被假定为可截断。这一点可以通过以下选项覆盖:
truncatable(boolean)-
该选项控制
postgres_fdw是否允许使用TRUNCATE命令截断外部表。它可为外部表或外部服务器指定。表级选项会覆盖服务器级选项。默认值为true。当然,如果远程表实际上不可截断,最终仍会报错。该选项的主要作用是允许在本地直接抛出错误,而无需查询远程服务器。
F.38.1.9. 导入选项
F.38.1.9. 导入选项
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. 连接管理选项
F.38.1.10. 连接管理选项
默认情况下,postgres_fdw 与外部服务器建立的所有连接都会在本地会话中保持打开,以便重复使用。
keep_connections(boolean)-
该选项控制
postgres_fdw是否保持与外部服务器的连接处于打开状态,以便后续查询重用它们。该选项只能为外部服务器指定。默认值为on。如果设置为off,则到该外部服务器的所有连接都会在每个事务结束时被丢弃。 use_scram_passthrough(boolean)-
该选项控制
postgres_fdw在连接到外部服务器时是否使用 SCRAM 透传认证。该选项可为外部服务器或用户映射指定。用户映射的设置会覆盖外部服务器的设置。使用 SCRAM 透传认证时,postgres_fdw使用经 SCRAM Hash 处理的凭据,而不是明文用户密码连接远程服务器。这样可以避免在 PostgreSQL 系统目录中存储明文用户密码。要使用 SCRAM 透传认证:
-
远程服务器必须请求
scram-sha-256认证方法;否则连接将失败。 -
远程服务器可以是任何支持 SCRAM 的 PostgreSQL 版本。对
use_scram_passthrough的支持只要求客户端一侧(FDW 侧)具备。 -
用户映射密码不会被使用。
-
运行
postgres_fdw的服务器与远程服务器,必须针对用于在postgres_fdw上认证到外部服务器的该用户,拥有完全相同的 SCRAM 凭据(加密密码)(盐值和迭代次数都必须相同,而不仅仅是密码相同)。因而,如果要建立到多个主机的 FDW 连接,例如用于分区外部表或分片,则所有主机都必须为相关用户保存完全相同的 SCRAM 凭据。
-
发起对外 FDW 连接的 PostgreSQL 实例中,当前会话的传入客户端连接也必须使用 SCRAM 认证。(因此称为“透传”:SCRAM 必须在进入和离开时都被使用。)这是 SCRAM 协议的技术要求。
-
postgres_fdw_get_connections( IN check_conn boolean DEFAULT false, OUT server_name text, OUT user_name text, OUT valid boolean, OUT used_in_xact boolean, OUT closed boolean, OUT remote_backend_pid int4) returns setof record-
此函数返回 postgres_fdw 从本地会话到外部服务器所建立的所有打开连接的信息。如果没有打开的连接,则不返回任何记录。
check_conn 为 true 时,此函数会检查每个连接的状态,并在 closed 列中显示结果。此功能目前仅适用于支持 poll 系统调用的非标准 POLLRDHUP 扩展的系统,包括 Linux。它可用于检查事务中使用的全部连接是否仍然打开。只要有连接关闭,事务就无法成功提交,因此检测到连接关闭时应尽快回滚,而非继续执行至结束。如果函数报告某个连接的 used_in_xact 和 closed 均为 true,用户可以立即回滚事务。
此函数的用法示例:
postgres=# SELECT * FROM postgres_fdw_get_connections(true); server_name | user_name | valid | used_in_xact | closed | remote_backend_pid -------------+-----------+-------+--------------+----------------------------- loopback1 | postgres | t | t | f | 1353340 loopback2 | public | t | t | f | 1353120 loopback3 | | f | t | f | 1353156
输出列见表 F.28。
表 F.28.
postgres_fdw_get_connections输出列列 类型 说明 server_name文本 此连接的外部服务器名称。如果服务器已被删除但连接仍保持打开(即被标记为无效),则该值为 NULL。user_name文本 映射到此连接所属外部服务器的本地用户名称;如果使用的是 public 映射,则为 public。如果用户映射已被删除但连接仍保持打开(即被标记为无效),则该值为NULL。有效 boolean如果此连接无效,则为假;无效意味着它在当前事务中被使用,但其外部服务器或用户映射已被更改或删除。无效连接将在事务结束时关闭。否则返回真。 used_in_xactboolean如果此连接在当前事务中被使用,则为真。 封闭 boolean此连接已关闭时为 true,否则为 false。如果 check_conn 设为 false,或当前平台不支持连接状态检查,则返回 NULL。 remote_backend_pidint4外部服务器上处理此连接的远程后端进程 ID。如果远程后端已终止且连接已关闭( closed为true),此处仍会显示该已终止后端的进程 ID。
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
postgres_fdw 在首次执行使用与某个外部服务器关联的外部表的查询时,会建立到该外部服务器的连接。默认情况下,该连接会在同一会话中保留并供后续查询重用。这一行为可通过外部服务器的 keep_connections 选项控制。如果使用多个用户标识(用户映射)访问该外部服务器,则会为每个用户映射建立一个连接。
当更改外部服务器或用户映射的定义,或将其删除时,相关连接会被关闭。但请注意,如果任何连接在当前本地事务中正在使用,则会保留到事务结束。关闭的连接会在后续使用外部表的查询需要它们时重新建立。
一旦与外部服务器建立连接,默认情况下它会一直保留到本地会话或对应的远程会话退出。要显式断开连接,可以禁用外部服务器的 keep_connections 选项,或使用 postgres_fdw_disconnect 和 postgres_fdw_disconnect_all 函数。例如,这些函数可用于关闭不再需要的连接,从而释放外部服务器上的连接资源。
在引用某个外部服务器上任意远程表的查询期间,如果当前本地事务尚未在该远程服务器上打开对应的事务,postgres_fdw 就会在该远程服务器上打开一个事务。本地事务提交或中止时,远程事务也会提交或中止。保存点也会通过创建对应的远程保存点进行类似管理。
当本地事务的隔离级别为 SERIALIZABLE 时,远程事务使用 SERIALIZABLE;否则使用 REPEATABLE READ 隔离级别。这一选择确保如果一个查询在远程服务器上执行多次表扫描,所有扫描都能获得快照一致的结果。其结果是,同一事务中的后续查询会看到来自远程服务器的相同数据,即使远程服务器由于其他活动正在发生并发更新。对于使用 SERIALIZABLE 或 REPEATABLE READ 隔离级别的本地事务,这种行为本来就符合预期;但对于 READ COMMITTED 本地事务,则可能令人意外。未来的 PostgreSQL 版本可能会修改这些规则。
请注意,postgres_fdw 当前不支持为两阶段提交预备远程事务。
postgres_fdw 会尽力优化远程查询,以减少从外部服务器传输的数据量。这是通过将查询的 WHERE 子句发送到远程服务器执行,以及不获取当前查询不需要的表列来实现的。为降低查询被错误执行的风险,只有当 WHERE 子句使用的所有数据类型、操作符和函数都是内置的,或属于外部服务器 extensions 选项列出的扩展时,才会将该子句发送到远程服务器。这类子句中的操作符和函数还必须是 IMMUTABLE。对于 UPDATE 或 DELETE 查询,postgres_fdw 会在查询中不存在无法发送到远程服务器的 WHERE 子句、没有本地连接操作、目标表上没有行级本地 BEFORE 或 AFTER 触发器或存储生成列,也没有来自父视图的 CHECK OPTION 约束时,尝试将整个查询发送到远程服务器以优化执行。在 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 - 对于 9.0 及更新版本的远端服务器,extra_float_digits 设为 3;更早版本则设为 2。
这些设置通常不像 search_path 那样容易出问题,但如果有需要,也可以通过函数的 SET 选项处理。
不建议通过修改这些参数的会话级设置来覆盖这种行为;这很可能导致 postgres_fdw 工作异常。
postgres_fdw 可用于访问 PostgreSQL 8.3 及更新的远程服务器;只读能力可追溯到 8.1。
一个限制是,postgres_fdw 通常认为外部表 WHERE 子句中的不可变内置函数和运算符可以安全地发送到远程服务器执行。因此,远程服务器所用版本之后才加入的内置函数也可能被发送过去,导致“function does not exist”或类似错误。可以通过改写查询绕过此问题,例如将外部表引用放入带 OFFSET 0 的子 SELECT 中,以此形成优化屏障,并将有问题的函数或运算符放在子 SELECT 外部。
另一个限制是,在外部表上执行 INSERT 语句并带有 ON CONFLICT DO NOTHING 子句时,远程服务器必须运行 PostgreSQL 9.5 或更高版本,因为更早版本不支持此特性。
postgres_fdw 可以在等待事件类型 Extension 下报告以下等待事件:
PostgresFdwCleanupResult-
等待远程服务器上的事务中止。
PostgresFdwConnect-
等待与远程服务器建立连接。
PostgresFdwGetResult-
等待接收来自远程服务器的查询结果。
postgres_fdw.application_name(string)-
指定用于application_name配置参数的值,该值会在
postgres_fdw建立到外部服务器的连接时使用。这会覆盖服务器对象的application_name选项。请注意,修改此参数不会影响任何现有连接,除非这些连接重新建立。postgres_fdw.application_name可以是任意长度的任意字符串,甚至可以包含非 ASCII 字符。不过,当它被传递并作为外部服务器中的application_name使用时,请注意它会被截断到少于NAMEDATALEN个字符。除可打印 ASCII 字符以外的所有字符都会被替换为C 风格的十六进制转义。有关细节见application_name。%字符表示“转义序列”的开始,并会按下述方式替换为状态信息。未识别的转义会被忽略。其他字符会原样复制到应用名中。请注意,不允许在%与选项字母之间指定正负号或数字字面量来进行对齐和填充。转义序列 效果 %a本地服务器上的应用名 %c本地服务器上的会话 ID(细节见log_line_prefix) %C本地服务器上的集簇名(细节见cluster_name) %u本地服务器上的用户名 %d本地服务器上的数据库名 %p本地服务器上后端的进程 ID %%字面字符 % 例如,假设用户 local_user 从数据库 local_db 以 foreign_user 身份连接到 foreign_db,则设置 'db=%d, user=%u' 会替换为 'db=local_db, user=local_user'。
下面是使用 postgres_fdw 创建外部表的一个示例。首先安装扩展:
CREATE EXTENSION postgres_fdw;
然后使用CREATE SERVER创建外部服务器。在本示例中,希望连接到一台 PostgreSQL 服务器,它运行在主机 192.83.123.89 上并监听 5432 端口。要连接的数据库在远程服务器上名为 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 更可取。
Shigeru Hanada <shigeru.hanada@gmail.com>
相关条目
文档与源码
来源构建
- 版本
- 18.6
- 构建
- PostgreSQL 18.6 source archive
- 来源指纹
555610c24d53e4316da5b7d3fc25c279d96856d5e0e23ee308c328c5fa881d9f
版本比较
PostgreSQL 10 → 11: 属性变化。
以下差异保留原始字段名与英文源描述。
--- PostgreSQL 10
+++ PostgreSQL 11
@@ -3,9 +3,11 @@
"AddForeignUpdateTargets": "postgresAddForeignUpdateTargets",
"AnalyzeForeignTable": "postgresAnalyzeForeignTable",
"BeginDirectModify": "postgresBeginDirectModify",
+ "BeginForeignInsert": "postgresBeginForeignInsert",
"BeginForeignModify": "postgresBeginForeignModify",
"BeginForeignScan": "postgresBeginForeignScan",
"EndDirectModify": "postgresEndDirectModify",
+ "EndForeignInsert": "postgresEndForeignInsert",
"EndForeignModify": "postgresEndForeignModify",
"EndForeignScan": "postgresEndForeignScan",
"ExecForeignDelete": "postgresExecForeignDelete",
比较已记录的接口与属性,排除来源指纹和构建元数据。某个样本中没有记录,不能据此判断实际引入或移除的版本。
导出 JSON · 返回外部数据包装器 · 收录范围为 PostgreSQL 10 至 20;最早采样版本不一定是实际引入版本。