Postgresql Linked server远程服务器取数据的执行计划原理

本文来自postgresql官方文档
https://www.postgresql.org/docs/11/postgres-fdw.html
http://postgres.cn/docs/11/postgres-fdw.html

代价估计选项
postgres_fdw通过在远程服务器上执行查询来检索远程数据,因此理想的扫描一个外部表的估计代价应该是在远程服务器上完成它的花销,外加一些通信开销。得到这样一个估计的最可靠的方法是询问远程服务器并加上一些通信开销 — 但是对于简单查询,不值得为获得一个代价估计而额外使用一次远程查询。因此postgres_fdw提供了下列选项来控制如何完成代价估计

use_remote_estimate
这个选项控制postgres_fdw 是否发出远程 EXPLAIN 命令以获得成本估算,它可以为一个外部表或一个外部服务器指定。一个外部表的设置会覆盖它的服务器的任何设置,但是只用于这个表。默认值是false。

fdw_startup_cost
这个选项是一个要被加到那个服务器上所有外部表扫描的估计启动代价的数字值。这表示在远程端建立连接、解析和规划查询等的额外开销。默认值为 100。

fdw_tuple_cost
这个选项是一个数字值,它被用作那个服务器上外部表扫描的每元组额外成本,它可以为一个外部服务器指定。这表示在服务器之间数据传输的额外负荷。你可以增加或减少这个数来反映到远程服务器更高或更低的网络延迟。默认值是0.01。

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

远程执行选项
默认情况下,只有使用了内建操作符和函数的WHERE子句才会被考虑在远程服务器上执行。涉及非内建函数的子句将会在取完行后在本地进行检查。如果这类函数在远程服务器上可用并且可以用来产生和本地执行时一样的结果,则可以通过将这种WHERE子句发送到远程执行来提高性能。可以用下面的选项控制这种行为:

extensions
这个选项是一个用逗号分隔的已安装的PostgreSQL扩展名称列表,这些扩展在本地和远程服务器上具有兼容的版本。属于一个该列表中扩展的 immutable 函数和操作符将被考虑转移到远程服务器上执行。这个选项只能为外部服务器指定,无法逐个表指定。在使用extensions选项时,用户应该负责确保列出的扩展在本地和远程服务器上都存在且保持一致。否则,远程查询可能失败或者行为异常。

fetch_size
这个选项指定在每次获取行的操作中postgres_fdw应该得到的行数。可以为一个外部表或者外部服务器指定这个选项。在表上指定的选项将会覆盖在服务器级别上指定的选项。默认值为100。

远程查询优化
postgres_fdw尝试优化远程查询来减少从外部服务器传来的数据量。这可以通过把查询的WHERE子句发送给远程服务器执行来完成,并且还可以不检索当前查询不需要的表列。为了降低错误执行查询的风险,WHERE 子句不会发送到远程服务器,除非它们仅使用内置的或属于外部服务器扩展选项中列出的扩展的数据类型、运算符和函数。这些子句中的操作符合函数也必须是IMMUTABLE。对于UPDATE或者DELETE查询, 如果没有不能发送给远程服务器的WHERE子句、 没有查询的本地连接、目标表上没有本地的行级BEFORE或AFTER触发器, 并且没有来自父视图的CHECK OPTION约束,postgres_fdw会尝试通过将整个查询发送给远程服务器来优化查询的执行。在UPDATE中,赋值给目标列的表达式只能使用内建数据类型、IMMUTABLE操作符或者IMMUTABLE操作符,这样能降低查询被误执行的风险。

当postgres_fdw碰到同一个外部服务器上的外部表之间的连接时,它会把整个连接发送给外部服务器,除非由于某些原因它认为逐个从每一个表取得行的效率更高或者涉及的表引用属于不同的用户映射。在发送JOIN子句时,它也会采取和上述WHERE子句相同的预防措施。

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

来自TDS_FDW(PG到Sqlserver的linked server组件)的官方文档说明
https://github.com/tds-fdw/tds_fdw/blob/master/ForeignTableCreation.md
https://github.com/tds-fdw/tds_fdw/blob/master/src/tds_fdw.c

use_remote_estimate
Whether we estimate the size of the table by performing some operation on the remote server (as defined by row_estimate_method), or whether we just use a local estimate, as defined by local_tuple_estimate.
我们是否通过在远程服务器上执行一些操作来估计表的大小(由 row_estimate_method 定义),或者我们是否只使用本地估计,如 local_tuple_estimate 所定义。

local_tuple_estimate
A locally set estimate of the number of tuples that is used when use_remote_estimate is disabled.
禁用 use_remote_estimate 时使用的元组数量的本地设置估计。

row_estimate_method
Default: execute
This can be one of the following values:
execute: Execute the query on the remote server, and get the actual number of rows in the query.
在远程服务器上执行查询,并获取查询中的实际行数。
showplan_all: This gets the estimated number of rows using MS SQL Server’s SET SHOWPLAN_ALL.
使用 MS SQL Server 的 SET SHOWPLAN_ALL 获取估计的行数。

tds_fdw没有postgresql_fdw中的远程连接选项fetch_size,如下
ocmportfolioDB=# CREATE SERVER mssql_svrtest1 FOREIGN DATA WRAPPER tds_fdw OPTIONS (servername ‘172.22.100.103’, port ‘1433’, database ‘Wondb’,fetch_size ‘20’);
ERROR: Invalid option “fetch_size”
HINT: Valid options in this context are: servername, language, character_set, port, database, dbuse, tds_version, msg_handler, row_estimate_method, use_remote_estimate, fdw_startup_cost, fdw_tuple_cost

TDS_FDW外部大表limit 1很慢的一个案例

现象1
1、查询外部表where osid<10003,返回15074行到postgresql服务端再展示15074行给客户端,总计需要180ms
2、查询外部表where osid<10003 limit 1,返回15074行到postgresql端服务端再展示1行给客户端,总计需要75ms
3、查询外部表limit 1没有where条件,返回129348086行到postgresql端服务端再展示1行给客户端,总计需要245000ms

现象2
postgresql查询外部表“select * from won.SDHSFTmp limit 1” 和 sqlserver的 “select top 1 * from dbo.SDHsfTmp”很类似,但是
sqlserver端直接查询“select top 1 * from dbo.SDHsfTmp”很快,postgresql查询外部表“select * from won.SDHSFTmp limit 1” 很慢

原因是因为:因为postgresql的limit 1语句在sqlserver中并不存在,所以当postgresql端的limit 1没有where条件,则postgresql端的语句压根不会通过tds_fdw转化为sqlserver对应的select top 1 这样的语句,这样结果就是sqlserver远端只能直接查询整表再把整表结果返回给postgresql端,由postgresql接受了整表结果再取1行

ocmportfolioDB=# explain analyze select * from won.SDHSFTmp where osid<10003;
NOTICE:  tds_fdw: Query executed correctly
NOTICE:  tds_fdw: Getting results
                                                       QUERY PLAN
-------------------------------------------------------------------------------------------------------------------------
 Foreign Scan on sdhsftmp  (cost=200.00..1507650.74 rows=15074 width=100) (actual time=1.117..81.374 rows=15074 loops=1)
 Planning Time: 71.005 ms
 Execution Time: 101.006 ms
(3 rows)
ocmportfolioDB=# explain analyze select * from won.SDHSFTmp where osid<10003 limit 1;
NOTICE:  tds_fdw: Query executed correctly
NOTICE:  tds_fdw: Getting results
                                                        QUERY PLAN
--------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=200.00..300.00 rows=1 width=100) (actual time=1.237..1.239 rows=1 loops=1)
   ->  Foreign Scan on sdhsftmp  (cost=200.00..1507650.74 rows=15074 width=100) (actual time=1.234..1.235 rows=1 loops=1)
 Planning Time: 55.918 ms
 Execution Time: 19.656 ms
(4 rows)
ocmportfolioDB=# explain analyze select * from won.SDHSFTmp limit 1;
NOTICE:  tds_fdw: Query executed correctly
NOTICE:  tds_fdw: Getting results
                                                            QUERY PLAN
----------------------------------------------------------------------------------------------------------------------------------
 Limit  (cost=200.00..300.01 rows=1 width=100) (actual time=4.995..4.998 rows=1 loops=1)
   ->  Foreign Scan on sdhsftmp  (cost=200.00..12936102180.86 rows=129348086 width=100) (actual time=4.992..4.993 rows=1 loops=1)
 Planning Time: 244993.104 ms
 Execution Time: 78.929 ms
(4 rows)

猜你喜欢

转载自blog.csdn.net/lusklusklusk/article/details/120023138
今日推荐