文章目录
- 0.简介
- 1.FDW介绍
- 2.使用方式
- 2.1 创建过程
- 2.1.1 创建插件
- 2.1.2 创建 Foreign Server
- 2.1.3 创建 User Mapping(外部服务器映射,本地文件可以不需要)
- 2.1.4 创建外部表
- 2.2 查询流程
- 3.源码分析
- 3.1 扩展接口分析
- 3.2 和其他部分关联
- 3.2.1 和计划的关联
- 3.2.2 和执行器关联
- 3.2.3 FDW异步特性
- 4.fdw插件扩展总结
0.简介
PG具有很好的扩展性,通过代码内部使用的函数指针,不同层级之间的标准接口,通过SQL语句创建的不同配置等实现其不改动核心代码而直接的扩展,本文是PG插件部分的第一篇,主要介绍FDW插件,从FDW插件的角度来分析PG的通过插件实现功能扩展。
1.FDW介绍
FDW(Foreign Data Wrapper,外部数据包装器),PG可以通过FDW扩展来操作当前数据库以外的数据,外部数据源可以是:文件,关系型的数据(PG,MySQL,Oracle等),非关系型数据库等,目前详细的支持可见:
https://wiki.postgresql.org/wiki/Foreign_data_wrappers。
用户可以通过在PG中创建外部表(Foreign Table),外部表将作为代理,用于访问外部数据源,然后转换为PG的结果形式,在使用上查询一个外部的数据源和内部的表没有区别。
FDW包含四个部分:
1) Foreign Data Wrapper:特定于各数据源的库,定义了如何建立与外部数据源的连接、执行查询及处理其他操作。如postgres_fdw用于连接其他 PostgreSQL 服务器,file_fdw用于连接普通的文本文件。
2) Foreign Server:在本地 PostgreSQL 中定义一个外部服务器对象,对应实际的远程或非本地数据存储实例。
3) User Mapping:为每个外部服务器设置用户映射,明确哪些本地用户有权访问,并提供相应的认证信息,如用户名和密码。
4) Foreign Table:在本地数据库创建表结构,作为外部数据源中表的映射。
2.使用方式
本节使用file_fdw为例介绍创建和查询。
2.1 创建过程
2.1.1 创建插件
postgres=# create extension file_fdw;
2.1.2 创建 Foreign Server
postgres=# CREATE SERVER pglog FOREIGN DATA WRAPPER file_fdw;
2.1.3 创建 User Mapping(外部服务器映射,本地文件可以不需要)
//例子如下
//创建用户user01 与 远端用户user02的映射
CREATE USER MAPPING
FOR user01
server db02
options(user 'user02',password 'user02');
2.1.4 创建外部表
CREATE FOREIGN TABLE pglog (
log_time timestamp(3) with time zone,
user_name text,
database_name text,
process_id integer,
connection_from text,
session_id text,
session_line_num bigint,
command_tag text,
session_start_time timestamp with time zone,
virtual_transaction_id text,
transaction_id bigint,
error_severity text,
sql_state_code text,
message text,
detail text,
hint text,
internal_query text,
internal_query_pos integer,
context text,
query text,
query_pos integer,
location text,
application_name text
) SERVER pglog
OPTIONS ( filename '/pgdata/log/pglog.csv', format 'csv' );
2.2 查询流程
查询和内部表使用一致,直接使用select * from xxx即可。
3.源码分析
3.1 扩展接口分析
对于访问外部数据源,主要是通过FdwRoutine这一结构体来进行,任何接入外部数据源的插件都可以根据自身需要去实现这些接口。
typedef struct FdwRoutine
{
NodeTag type;
/* Functions for scanning foreign tables */
GetForeignRelSize_function GetForeignRelSize;
GetForeignPaths_function GetForeignPaths;
GetForeignPlan_function GetForeignPlan;
BeginForeignScan_function BeginForeignScan;
IterateForeignScan_function IterateForeignScan;
ReScanForeignScan_function ReScanForeignScan;
EndForeignScan_function EndForeignScan;
/*
* Remaining functions are optional. Set the pointer to NULL for any that
* are not provided.
*/
/* Functions for remote-join planning */
GetForeignJoinPaths_function GetForeignJoinPaths;
/* Functions for remote upper-relation (post scan/join) planning */
GetForeignUpperPaths_function GetForeignUpperPaths;
/* Functions for updating foreign tables */
AddForeignUpdateTargets_function AddForeignUpdateTargets;
PlanForeignModify_function PlanForeignModify;
BeginForeignModify_function BeginForeignModify;
ExecForeignInsert_function ExecForeignInsert;
ExecForeignUpdate_function ExecForeignUpdate;
ExecForeignDelete_function ExecForeignDelete;
EndForeignModify_function EndForeignModify;
BeginForeignInsert_function BeginForeignInsert;
EndForeignInsert_function EndForeignInsert;
IsForeignRelUpdatable_function IsForeignRelUpdatable;
PlanDirectModify_function PlanDirectModify;
BeginDirectModify_function BeginDirectModify;
IterateDirectModify_function IterateDirectModify;
EndDirectModify_function EndDirectModify;
/* Functions for SELECT FOR UPDATE/SHARE row locking */
GetForeignRowMarkType_function GetForeignRowMarkType;
RefetchForeignRow_function RefetchForeignRow;
RecheckForeignScan_function RecheckForeignScan;
/* Support functions for EXPLAIN */
ExplainForeignScan_function ExplainForeignScan;
ExplainForeignModify_function ExplainForeignModify;
ExplainDirectModify_function ExplainDirectModify;
/* Support functions for ANALYZE */
AnalyzeForeignTable_function AnalyzeForeignTable;
/* Support functions for IMPORT FOREIGN SCHEMA */
ImportForeignSchema_function ImportForeignSchema;
/* Support functions for parallelism under Gather node */
IsForeignScanParallelSafe_function IsForeignScanParallelSafe;
EstimateDSMForeignScan_function EstimateDSMForeignScan;
InitializeDSMForeignScan_function InitializeDSMForeignScan;
ReInitializeDSMForeignScan_function ReInitializeDSMForeignScan;
InitializeWorkerForeignScan_function InitializeWorkerForeignScan;
ShutdownForeignScan_function ShutdownForeignScan;
/* Support functions for path reparameterization. */
ReparameterizeForeignPathByChild_function ReparameterizeForeignPathByChild;
} FdwRoutine;
可以看到,其内部包含的常见操作有开始扫描(BeginForeignScan),执行扫描(IterateForeignScan)等,还有insert、update、delete相关的接口。
下面还是以file_fdw为例来看,其主要实现了扫描使用的接口和用于分析性能的接口。
Datum
file_fdw_handler(PG_FUNCTION_ARGS)
{
FdwRoutine *fdwroutine = makeNode(FdwRoutine);
fdwroutine->GetForeignRelSize = fileGetForeignRelSize;
fdwroutine->GetForeignPaths = fileGetForeignPaths;
fdwroutine->GetForeignPlan = fileGetForeignPlan;
fdwroutine->ExplainForeignScan = fileExplainForeignScan;
fdwroutine->BeginForeignScan = fileBeginForeignScan;
fdwroutine->IterateForeignScan = fileIterateForeignScan;
fdwroutine->ReScanForeignScan = fileReScanForeignScan;
fdwroutine->EndForeignScan = fileEndForeignScan;
fdwroutine->AnalyzeForeignTable = fileAnalyzeForeignTable;
fdwroutine->IsForeignScanParallelSafe = fileIsForeignScanParallelSafe;
PG_RETURN_POINTER(fdwroutine);
}
3.2 和其他部分关联
对于查询计划计划和执行器部分如何获取并使用对应的FdwRoutine结构,在foreign.c中实现了多种查找方式:
/* Functions in foreign/foreign.c */
extern FdwRoutine *GetFdwRoutine(Oid fdwhandler);
extern Oid GetForeignServerIdByRelId(Oid relid);
extern FdwRoutine *GetFdwRoutineByServerId(Oid serverid);
extern FdwRoutine *GetFdwRoutineByRelId(Oid relid);
extern FdwRoutine *GetFdwRoutineForRelation(Relation relation, bool makecopy);
3.2.1 和计划的关联
和计划关联主要体现在优化阶段的调用:
3.2.2 和执行器关联
PG内核执行器使用的是火山模型,主要包含三个阶段:初始化,执行,清理,和FDW关联如下:
3.2.3 FDW异步特性
该特性就是在执行Append算子时,对所有外部表子计划发起异步请求,然后开始执行本地计划,不阻塞等待外部表子计划的结果,通过轮询I/O事件来获取结果,通过这种方式,尽可能的并行,减少等待。
4.fdw插件扩展总结
通过抽象FdwRoutine结构来支持扩展,只需要对于不同的外部数据源实现不同的FdwRoutine就能实现不同外部数据源的对接。