對于一定規模的系統而言,數據倉庫往往需要訪問外部數據來完成分析和計算。外部數據包裝器(Foreign Data Wrapper, 簡稱 FDW)是 PostgreSQL 提供的訪問外部數據源機制。用戶可以使用簡單的 SQL 語句訪問和操作外部數據源,就像操作本地表一樣。在上次直播中,我們深入探討了 PostgreSQL FDW 的基本概念、詳細使用方法、實現原理以及源碼實現。以下是根據直播內容整理成稿。
01 FDW 使用詳解
FDW 是 PostgreSQL 中的一項關鍵特性,它賦予數據庫用戶直接通過 SQL 語句訪問存儲于外部數據源的能力。FDW 遵循 SQL/MED 標準設計,使 PostgreSQL 能夠無縫對接多種異構數據庫系統以及非數據庫類數據源。FDW 可以用于以下場景:1. 跨數據庫查詢:在 PostgreSQL 數據庫中,我們可以通過 FDW 直接請求和查詢其他 PostgreSQL 實例,或是其他數據庫如 MySQL、Oracle、DB2、SQL Server 等。2. 數據整合:當我們需要從不同數據源整合數據時,例如 REST API、文件系統、NoSQL 數據庫以及流式系統等,FDW 能夠幫助我們輕松實現這種跨來源的數據整合。3. 數據遷移:利用 FDW,我們可以高效地將數據從舊系統遷移到新的 PostgreSQL 數據庫中。4. 實時數據訪問:通過 FDW,我們能夠訪問外部實時更新的數據源。
PostgreSQL 支持非常多常見的 FDW,能夠直接訪問多種類型的外部數據源。例如,可以連接并查詢遠程的 PostgreSQL,或者主流的 SQL 數據庫如 Oracle、MySQL、DB2 以及 SQL Server。同時,PostgreSQL FDW 也具備靈活的接口,支持用戶自定義外部訪問方式。表 1。常見的 FDW—SQL Database
此外,對于 NoSQL 數據庫,如 HBase、Cassandra、ClickHouse,以及實時數據庫如 InfluxDB、消息隊列如 Kafka、文檔型數據庫如 MongoDB 等等都能通過 FDW 實現數據訪問。表 2。常見的 FDW—NoSQL Database
常見的文本格式數據,如 CSV、JSON、Parquet 和 XML,也可以通過 FDW 輕松訪問。大數據組件如 Elasticsearch、BigQuery,以及 Hadoop 生態系統中的 HDFS 和 Hive 等等都可以通過 FDW 實現無縫集成。表 3。常見的 FDW—File Wrapper
表 4。常見的 FDW—Big Data
FDW 機制由四個核心組件構成:1. Foreign Data Wrapper:特定于各數據源的庫,定義了如何建立與外部數據源的連接、執行查詢及處理其他操作。例如,postgres_fdw用于連接其他 PostgreSQL 服務器,mysql_fdw則專門連接 MySQL 數據庫。2. Foreign Server:在本地 PostgreSQL 中定義一個外部服務器對象,對應實際的遠程或非本地數據存儲實例。3. User Mapping:為每個外部服務器設置用戶映射,明確哪些本地用戶有權訪問,并提供相應的認證信息,如用戶名和密碼。4. Foreign Table:在本地數據庫創建表結構,作為外部數據源中表的映射。對這些外部表發起的 SQL 查詢將被轉換并傳遞給相應的 FDW,在外部數據源上執行。接下來,我們以常見的 postgres_fdw 為例,來簡要探討一下 FDW 的基本使用方法。
- 步驟一:創建插件
test=create extension postgres_fdw; CREATE EXTENSION
- 步驟二:創建 Foreign Server
CREATE SERVER foreign_server FOREIGN DATA WRAPPER postgres_fdw OPTIONS (host '127.0.0.1', port '8001', dbname 'postgres');
- 步驟三:創建 User Mapping
CREATE USER MAPPING FOR gpadmin SERVER foreign_server;
- 步驟四:創建外部表
CREATE FOREIGN TABLE foreign_table ( val int ) SERVER foreign_server OPTIONS (schema_name 'public', table_name 't2');
02 FDW 實現原理
在 PostgreSQL 的內核代碼中,FDW 訪問外部數據源的操作接口主要通過 FdwRoutine 這一結構體進行定義。任何接入外部數據源的插件都可以根據自身需要去實現這些接口。圖 1.FdwRoutine 定義了外部數據操作的接口
接口函數大致分為多個類別,包括但不限于掃描、修改、分析外部表等等。例如,掃描外部表相關接口定義了如何掃描外部表,常見的操作包括開始掃描( BeginForeignScan,主要進行準備工作)、執行掃描(IterateForeignScan,從掃描中獲取數據)、重新掃描(RescanForeignScan)以及結束掃描(EndForeignScan)等。圖 2。掃描外部表相關接口
此外,還有用于修改數據的外部表接口,支持對數據進行 insert、delete、update 等操作,以及 explain 和 analyze 等外部表接口。insert/delete/update 外部表接口
explain/analyze 外部表接口
注:需要明確的是,當實現一個訪問外部數據源的 FDW 時,并不需要實現以上提到所有的外部訪問數據接口。開發者只需根據實際需求實現對應的接口即可。例如,如果只訪問查詢某個 Web 數據源而不進行修改或刪除操作,那么就不需要實現關于刪改和更新的操作接口,只需實現 SCAN 掃描相關的接口即可。
如下圖,這是一個 FDW 插件實現 FdwRoutine 的示例,這里僅實現了一些基礎的掃描操作接口(如BeginForeignScan、IterateForeignScan等),以及用于性能分析的AnalyzeForeignTable接口。插件實現 FdwRoutine在 PostgreSQL 的執行過程中,這些接口函數會在 planner 或 executor 階段被調用。尤其是當 executor 需要依賴外部服務插件訪問數據時,它會通過插件提供的數據訪問接口來獲取數據。這使得 FDW 能夠與 PostgreSQL 的 Parser、Planner 以及 Rewriter 等組件能夠無縫協作。
在需要訪問外部數據源時,我們只需定義好相應的數據訪問接口,就能直接獲取數據,并按照 PostgreSQL 的標準流程進行后續處理。在執行過程中,執行器會分解為幾個階段進行:1、首先進入 init 階段,核心任務是執行外部表掃描ExecInitForeignScan。在這個階段,主要是定義了一些外部掃描的接口,并調用 FdwRoutine 中用戶自定義的接口,從而進行掃描前的準備。
執行 scan 前的準備
2、緊接著是執行查詢階段,此時會調用ExecuteForeignScan 方法。在這個方法中,我們主要需指定ForeignNext 來獲取下一組數據,并定義 ForeignRecheck 來檢驗數據元組的可見性。執行查詢階段
3、最后進入結束查詢階段,即執行 EndForeignScan,該階段主要負責資源清理工作。若系統檢測到存在 FDWRoutine,就會利用用戶自定義的 EndForeignScan 函數來釋放資源。結束查詢階段以上就是 FDW 整體的實現流程。接下來,為了更深入地了解 FDW 的工作機制,我們將深入探討 FDW 的源碼。
03 FDW 源碼解析
FDW 支持的數據類型眾多,但在此我們以常見的Postgres_fdw為例,剖析其源碼實現,同樣可幫助理解其他 FDW 的源碼邏輯。
FdwRoutine 定義
首先,我們需要定義 FdwRoutine。前文提到了 FdwRoutine 主要負責定義外部數據掃描的接口,接口需要自定義實現外部掃描的方法。FdwRoutine 定義
訪問外部數據源
定義好 FdwRoutine 之后,開始訪問并掃描外部數據源。在Postgres_fdw中,流程也就是進入BeginForeign Scan階段。這一階段主要是獲取我們先前定義的外部表實例和用戶信息,然后初始化并獲取一個連接到遠端數據源。postgresBeginForeignScan
執行查詢階段
獲取連接后,執行查詢,即進行 IterateForeignScan階段。這個過程的邏輯是創建一個游標迭代器(cursor),并從 cursor 中持續獲取數據。postgresIterateForeignScan當全部數據迭代或掃描完成后,我們會釋放連接并關閉 cursor 等資源,通過自定義的EndForeignScan階段完成。
postgresEndForeignScan
insert 操作
對于 insert 操作,例如,在本地 PostgreSQL 數據庫中修改 Web 數據源,增加一條數據,需要訪問插入 Web 數據的接口。此操作先進入BeginForeignInsert階段,任務是構造 SQL 語句,通過預處理語句進行初始化,做好插入準備。postgresBeginForeignInsert
之后,進入ExecuteForeignInsert階段,執行數據插入,主要通過預處理語句傳遞參數,然后發送 SQL 到遠端執行。postgresExecForeignInsert最后,EndForeignInsert階段負責收尾和資源清理。
postgresEndForeignInsert
更新/刪除操作
更新和刪除操作的邏輯與插入類似。首先進入BeginDirectModify階段,進行數據修改前的準備,如構建查詢語句、獲取連接等。隨后執行修改操作,主要通過發送參數和查詢到遠端來執行。postgresBeginDirectModify
postgresIterateDirectModify
本次分享,我們為大家講解了 FDW 基本概念、使用場景、實現原理,以及源碼解析,文章篇幅有限,更多技術細節講解歡迎大家訪問 B 站 https://www.bilibili.com/video/BV1pf421X7pk/?share_source=copy_web&vd_source=9f256c264f70725955bae178113d4bee,觀看視頻回放,希望能與朋友們一起更好地理解和用好 FDW。