F.32. postgres_fdw

postgres_fdw模块提供外部数据封装器的功能,PostgreSQL通过 它可以访问存储在外部的 PostgreSQL服务器上的数据。

本模块提供的功能不但涵盖老版本中dblink模块实现的功能, 而且postgres_fdw提供更加透明和符合标准的语法来访问远程表,并在许多情况下 提供更好的性能。

使用postgres_fdw模块做远程访问的准备:

  1. 使用CREATE EXTENSION语句安装postgres_fdw.

  2. 使用CREATE SERVER语句,为每个需要连接的远程数据库 创建一个外部服务器对象。指定除了userpassword 以外的连接信息作为服务器对象的选项。

  3. 使用CREATE USER MAPPING语句,为每个需要通过外部服务器 访问的数据库创建用户映射。指定远程的和密码作为映射用户的userpassword

  4. 使用CREATE FOREIGN TABLE语句,为每个需要访问的远程表创建外部表。 创建的外部表的对应列必须与远程表匹配。也可以在外部表中使用与远程表不同的表名和列名, 但前提是你必须将正确的远程对象名作为创建外部表对象的选项。

上面的操作成功后就可以使用SELECT外部表的方式访问存储在 远程表中的数据了。同样INSERT, UPDATEDELETE操作都是可以执行的。(映射的远程用户需要有能做这些操作的权限)

建议外部表的字段和相关联的远程表使用相同的数据类型和校对规则,虽然 postgres_fdw允许在需要的时候进行字符类型的转换,但当数据 类型和校对规则不匹配的时候,由于远程服务器和本地服务器对于 WHERE条件的不同解释,也许会造成语义的错误。

需要注意的是一个外部表可以声明比和他关联的远程表更少的列,列的排序也可以 不同。和远程表列的关联用的是列名,和列的位置无关。

F.32.1. postgres_fdw中FDW选项

F.32.1.1. 连接选项

一个作为封装外部数据使用的外部服务器,可以使用libpq接受的连接字符串,详见 第 31.1.2 节,除了下面的选项是不允许使用的:

  • userpassword(将在用户映射中指定)

  • client_encoding(根据本地服务器编码自动设定)

  • fallback_application_name(设定为postgres_fdw

只有超级用户连接到外部服务器是不需要密码的,所以需要为映射的普通用户指定 password

F.32.1.2. 对象名称选项

这些设置选项被用来控制被发往远程postgres服务器中的sql语句对象名称。 在外部表的名和关联的远程表名不同时,用于设置关联。

schema_name

这个选项可以为外部表指定模式名,和远程服务器上的表做关联。如果忽略 这个选项,远程表本身的模式名会被外部表使用。

table_name

这个选项可以为外部表指定表名,和远程服务器上的表做关联。如果忽略 这个选项,远程表本身的表名会被外部表使用。

column_name

这个选项可以为外部表列指定表名,和远程服务器上的列做关联。如果忽略 这个选项,远程表本身的列名会被外部表使用。

F.32.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_costuse_remote_estimate。 当值为假的时候,成本估算方法只能是按照语句本地的执行成本加上 fdw_startup_costuse_remote_estimate。 除非本地表的统计信息和远程表统计信息的相同,否则本地的估算一般是不精确的。 在外部表执行ANALYZE操作来刷新远程表的统计信息, 这个操作会扫描远程表,使计算和存储统计信息就像在本地一样。在本地保存统计信息 可以减少远程表的每个查询的执行计划都造成系统开销。但是如果远程表被更新的频率 太高,本地的统计信息也会很快失去应有的作用。

F.32.1.4. 更新选型

默认情况下,所有postgres_fdw相关外部表都假设是可以更新的。应用时 优先下面的选项 功能:

updatable

这个选项用来控制外部表是否可以用INSERT,UPDATEDELETE来修改。该选项可以被外部表或者外部服务器指定。表级别的 选项优先于服务器级别的。默认值是true

当然如果一个远程表本身是不能用增删改的,那将会报错。错误直接会在本地抛出。 注意information_schema将会根据这个选项的设置显示一个外部表是 否可以增删改,而不会去检查远程服务器。

F.32.2. 连接管理

postgres_fdw会在第一个查询外部服务器关联的外部表时 建立和外部服务器的连接。这个连接会一直保持,而且在同一个会话中被重用。 如果涉及多个用户(用户映射)访问外部服务器时,每个用户映射都户建立一个连接。

F.32.3. 事务管理

当查询涉及外部服务器的远程表时,将在本地开启一个对应的事务, postgres_fdw将会在远程服务器也开启事务。 远程事务会和本地事务提交终保持同步。保存点也是一样。

当本地的事务隔离级别为SERIALIZABLE时,远程的事务隔离级别也使用 SERIALIZABLE。否者远程的隔离级别将是REPEATABLE READ。 这是为了保证如果一个查询涉及远程服器上多个表的扫描,所有的扫描都会得到一致 的快照结果。这样产生的结果是即使远程服务器上的数据由于其他的操作在更新,单 个事务查询返回的结果也是一样的。上述结果在本地事务隔离级别 SERIALIZABLEREPEATABLE READ无论怎样都是可以实现的,但是在READ COMMITTED 下可能会得到意想不到的结果。未来的PostgreSQL发行版本也许会 修改这些规则。

F.32.4. 远程查询优化

postgres_fdw尝试优化远程查询,以减少从外部服务器的数据传输量。通用将带 WHERE查询条件的语句传到远程服务器上执行和不取回与查询结果不相关 的列来实现。为了减少查询未被执行的风险,WHERE从句中都是内建的数据类型 操作和函数的时候才会被传到远程服务器上。在从句中的操作和函数必须是不可变的。

EXPLAIN VERBOSE可以用来检查被送到远程服务器上的查询执行的实际状况。

F.32.5. 版本兼容

postgres_fdw可以将PostgreSQL8.3版本以后的服务器 作为远程服务器使用。8.1,8,2的服务器只能提供读的功能。有这样个问题由于版本的差异 将where中的内建函数送到远程服务器上执行的时候,远程服务器由于版本低无法识别会报 "function does not exist"或者相似的错误。可以用从写sql的方式解决,我们嵌入 一个子查询sub-SELECT with OFFSET 0,将有问题的函数和操作移 出 sub-SELECT

F.32.6. 示例

这是一个用postgres_fdw创建外部表的示例。 首先安装该扩展:

CREATE EXTENSION postgres_fdw;

然后使用CREATE SERVER创建一个外部服务器。 在这个示例中,我们希望连接到主机为192.83.123.89PostgreSQL服务器,监听端口为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 serial NOT NULL,
        data text
)
        SERVER foreign_server
        OPTIONS (schema_name 'some_schema', table_name 'some_table');

CREATE FOREIGN TABLE中声明的字段的数据类型和其他属性匹配实际远程表是非常重要的。 字段名也必须匹配,除非你附加column_name选项到单独的字段以显示它们在远程表中的命名。

F.32.7. 作者

Shigeru Hanada