qidao123.com技术社区-IT企服评测·应用市场
标题:
深入分析 PostgreSQL 外部数据封装器(FDW)的 SELECT 查询执行机制
[打印本页]
作者:
伤心客
时间:
3 天前
标题:
深入分析 PostgreSQL 外部数据封装器(FDW)的 SELECT 查询执行机制
弁言
PostgreSQL 中的外部数据封装器(Foreign Data Wrapper, FDW)是一种扩展,允许您像访问 PostgreSQL 数据库中的表一样,访问和操作存储在外部数据源中的数据。FDW 使 PostgreSQL 能够与多种数据存储系统(包括关系型和非关系型)进行集成,并以统一的方式在 PostgreSQL 环境中呈现这些数据。将一个表拆分成多个位于远程的较小表的操作也称为 sharding。外部数据库节点偶尔被称为外部门片或简略为分片。在本文中,我们将探讨在使用 postgres_fdw 处理外部 SELECT 查询时,其内部的详细工作流程。
以下是一些配景历史:
2003 年,SQL 标准中新增了一个名为 SQL/MED(SQL 管理外部数据)的规范,该标准界说了远程访问数据库的SQL规范。
2011 年,PostgreSQL 9.1 版本发布,开始支持此标准的只读操作。
2013 年,新增了对写入操作的支持。
当前有许多不同的 FDW 可供使用,允许 PostgreSQL 连接到各种远程数据存储(包括其他关系型数据库管理系统到平面文件等)。
这些 FDW 大多数没有 PostgreSQL 全球开辟组(PGDG)的官方支持,一些项目仍处于 beta 阶段。请谨慎使用!
一些与 FDW 相干的文档:
https://wiki.postgresql.org/wiki/Foreign_data_wrappers
https://pgxn.org/tag/fdw/
https://www.postgresql.org/docs/15/fdwhandler.html
设置一个简单的 FDW 环境
在设置之前,请确保已将 postgresql_fdw 扩展编译并安装到您的 PostgreSQL 二进制安装路径中。我们将设置一个由 1 个协调节点和 4 个外部数据节点构成的分片环境。
初始化所有数据库实例
$ initdb -D c1
$ initdb -D d1
$ initdb -D d2
$ initdb -D d3
$ initdb -D d4
复制代码
编辑 d1 到 d4 节点的设置文件,并将它们的端标语分别更改为 5433、5434、5435、5436,同时保持 c1 节点运行在默认端口 5432。这样做的缘故原由是我们将在同一台机器上运行所有节点以便演示。
在 C1 节点:
postgres=# CREATE EXTENSION postgres_fdw;
postgres=# CREATE SERVER s1 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'postgres', host '127.0.0.1', port '5433');
postgres=# CREATE SERVER s2 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'postgres', host '127.0.0.1', port '5434');
postgres=# CREATE SERVER s3 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'postgres', host '127.0.0.1', port '5435');
postgres=# CREATE SERVER s4 FOREIGN DATA WRAPPER postgres_fdw OPTIONS (dbname 'postgres', host '127.0.0.1', port '5436');
postgres=# CREATE USER MAPPING for postgres SERVER s1 OPTIONS(user 'postgres');
postgres=# CREATE USER MAPPING for postgres SERVER s2 OPTIONS(user 'postgres');
postgres=# CREATE USER MAPPING for postgres SERVER s3 OPTIONS(user 'postgres');
postgres=# CREATE USER MAPPING for postgres SERVER s4 OPTIONS(user 'postgres');
postgres=# CREATE TABLE t (a INT, b TEXT) PARTITION BY RANGE;
postgres=# CREATE TABLE t_local PARTITION OF t FOR VALUES FROM (1) TO (1000);
postgres=# CREATE FOREIGN TABLE t_s1 PARTITION OF t FOR VALUES FROM (1001) TO (2000) SERVER s1 OPTIONS(schema_name 'public', table_name 't');
postgres=# CREATE FOREIGN TABLE t_s2 PARTITION OF t FOR VALUES FROM (2001) TO (3000) SERVER s1 OPTIONS(schema_name 'public', table_name 't');
postgres=# CREATE FOREIGN TABLE t_s3 PARTITION OF t FOR VALUES FROM (3001) TO (4000) SERVER s1 OPTIONS(schema_name 'public', table_name 't');
postgres=# CREATE FOREIGN TABLE t_s4 PARTITION OF t FOR VALUES FROM (4001) TO (5000) SERVER s1 OPTIONS(schema_name 'public', table_name 't');
复制代码
在所有数据节点:
postgres=# CREATE TABLE t (a INT, b TEXT);
复制代码
到此为止,我们完成了一个简单的 postgres_fdw 设置。当数据插入到 c1 节点的 t 表时,数据可以存储在 c1 节点本地(范围 1 到 1000),也可以根据范围值分发到外部节点(d1 ~ d4)。
从外部节点查询数据
Postgres_fdw 并不会简单地转发您的 SELECT 查询并将其发送到外部节点处理,等待响应。这种方式的性能极低。与常规的 SELECT 查询一样,外部查询也必要仔细的分析和规划,以确保性能。远程查询通常包括以下几个步调:
分析器 – 创建查询树
根据 pg_catalog.pg_class 和 pg_catalog.pg_foreign_table 中的外部表界说创建查询树。
连接到外部服务器
规划器从 pg_catalog.pg_foreign_server 和 pg_catalog.pg_user_mapping 获取网络和用户映射信息。
通过 FDW 连接到远程服务器。
Postgres_fdw 使用 libpq 连接到远程 PostgreSQL 数据库。
规划
规划器使用 EXPLAIN 命令创建计划树。
Postgres_fdw 支持使用 EXPLAIN 获取远程表的统计信息,但也可以使用本地默认值。
EXPLAIN 返回远程服务器的启动成本和总成本,允许规划器正确评估语句。
use_remote_estimate 参数可以通过 ALTER SERVER 修改(默认关闭)。
反分析
规划器生成的计划树通过 FDW 获取了远程服务器的扫描路径。
Postgres_fdw 负责分析计划树并重构一组恰当远程服务器的 SQL 语句。
扩展查询协议 + 游标
Postgres_fdw 使用扩展查询协议,并借助游标从远程节点检索或获取数据。
它使用可重复读的隔离级别,以确保在事务期间数据的一致性,不会被其他并发事务更改。
相干的 FDW API
在访问远程服务器执行查询之前,规划器会调用 IsForeignScanParallelSafe()、GetForeignPlan() 和 GetForeignPaths() 来了解远程数据的规模,并确定是否将 WHERE 条件评估本地执行或外部执行等。
准备好之后,使用 BeginForeignScan 和 IterateForeignScan 来获取一行或多行数据。
最后,通过 EndForeignScan 释放资源,包括释放已分配的内存、打开的文件和连接到外部数据源的连接等。
IsForeignScanParallelSafe()
是否支持并行扫描。假如支持,当执行大量数据的 SELECT 时,可以启动多个工作进程并行执行。
GetForeignRelSize()
获取远程表的大小估算。规划器在扫描开始前调用。
GetForeignPaths()
获取所有可能的远程表扫描执行路径,并将每个扫描路径添加到扫描路径列表中。
GetForeignPlan()
在查询规划的最后阶段,从选定的访问路径创建 ForeignScan 计划节点。
此函数非常紧张,它包含要输出的目标列表、执行的 WHERE 语句以及由谁执行。
BeginForeignScan()
准备执行远程扫描。负责扫描开始前的必要初始化工作。
IterateForeignScan()
从远程节点获取一行数据,并将其返回为元组槽,结束时返回 NULL。
EndForeignScan()
结束扫描并释放资源。释放通过 palloc 分配的内存,清理打开的文件和连接到远程服务器的连接。
总结
本文对 postgres_fdw 内部工作原理的快速概述,涵盖了 SELECT 查询的基本知识。
本文由博客一文多发平台
OpenWrite
发布!
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
欢迎光临 qidao123.com技术社区-IT企服评测·应用市场 (https://dis.qidao123.com/)
Powered by Discuz! X3.4