PostgreSQL · 實現分析 · PostgreSQL 10.0 並行查詢和外部表的結合
前言
大家都知道,PostgreSQL 近幾大版本中加入了很多 OLAP 相關特性。9.6 的並行掃描應該算最大的相關特性。在今年發布的 10.0 中,並行掃描也在不斷加強,新增了並行的索引掃描。
我們知道並行掃描是支持外部數據源的。在雲上,有很多存儲存儲產品可以以外部數據源的形式做數據庫的外部存儲。例如,阿裏雲的 OSS 和 AWS 的 S3 都是絕佳的外部數據源。雲上的 PostgreSQL 和他們的結合可以給用戶提供既廉價又高性能數存儲的方案。
另人欣喜的是,PostgreSQL 的外部表對外提供了可編程接口,並且支持並行掃描框架。利用它可以使 PostgreSQL 的外部數據源訪問效率得到質的提升。
技術鋪墊
並行查詢
並行查詢是 PostgreSQL 引入的一個大特性,它可以優化 SQL 語句的執行方式,從傳統的單一進程,最多使用單個 CPU 運算的模式,提升到多進程,協同完成工作的模式。
並行查詢消耗更多的硬件資源,大大提高了任務的執行效率。
在 PostgreSQL 中,一個 SQL 任務是否可以被並行化,可以通過查看 SQL 的執行計劃(Plan)的方式看到。
例如:
EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%';
QUERY PLAN
-------------------------------------------------------------------------------------
Gather (cost=1000.00..217018.43 rows=1 width=97)
Workers Planned: 2
-> Parallel Seq Scan on pgbench_accounts (cost=0.00..216018.33 rows=1 width=97)
Filter: (filler ~~ '%x%'::text)
(4 rows)
可以看到,上麵的 SQL 采用了並行的方式執行,它使用了2個額外的並行工作進程(共3個進程)完成工作。
- 並行 worker 主要完成順序掃描數據的和過濾數據的工作,符合條件的數據被發送給主進程。
- 主進程的 Gather 節點接受來自子進程的數據,再發給客戶端。
並行查詢的參數配置
合理的配置下列參數能讓 PostgreSQL 成功開啟並行查詢特性。
- max_worker_processes 整個實例允許的最大並行工作進程,它的值建議和實例所在主機的邏輯 CPU 相關
- max_parallel_workers_per_gather 單個 Gather 節點的並行度,讓單個 SQL 更快的執行,可以增大該參數的設置。
- force_parallel_mode 是否讓查詢優化器盡可能的選擇並行的執行方式。
外部表
外部表是 PostgreSQL 引入外部數據的入口,任何的外部數據源都可以使用該接口把數據引入到數據庫中。用戶可以像訪問表一樣讀寫外部數據源上的數據。
目前 PostgreSQL 支持的常見外部數據源有 MySQL Oracle PostgreSQL OSS S3 等。
PostgreSQL 在引入並行查詢時也支持了外部表的並行查詢,並擴展了之前的編程接口。
並行的外部表掃描實現分析
SQL 語句執行一般過程
一條 SQL 語句的執行通常經曆下麵的過程:
1) 語法分析和語意分析
2) 查詢優化
3) 查詢執行
外部表的掃描在階段 2 和 3 都有相應的操作
- 查詢優化階段,需要提供對應外部數據源的數據大小(行數和行寬度)等信息,用於優化器計算最優的查詢路徑
- 查詢執行階段,需要實現幾個回調函數,用於向執行器(executor)輸送以行(slot)為單位的數據,直到外部數據讀取完成。
並行查詢在傳統模式上的變化
並行查詢模式的引入,是 PostgreSQL 在傳統的 Pipeline 模式上的較大改動。
大致的改進點如下,這部分也是外部表的並行查詢模式實現所要注意的
一 查詢優化階段
提供給優化器並行模型的各類代價信息,參與優化器進行整體的代價評估。
當並行模式最終被確認為最優方案後,優化器會給出並行模式的執行計劃。
二 查詢執行器階段
執行器得到一個帶有並行執行節點的計劃,還要進行如下工作
- 啟動並行工作進程。
- 開辟相關共享內存結構,準備交換數據。
- 構造並行協作相關內存結構。
- 給工作進程下發並行相關的執行任務。
- 並行執行,並行工作進程拿到數據做相應的處理後發送給主進程的 Gather 節點,主進程的 Gather 節點拿到數據後返回給上層節點。直到所有數據處理完畢。
- 釋放資源,處理事物信息。
上訴工作中框架相關的通用工作 PostgreSQL 已經完成,我們需要在並行框架下實現各階段的部分邏輯。下麵將會重點說明這部分細節。
外表的並行查詢的實現分析
實現外部表的並行掃描需要注意上述環節中每個環節,下麵描述概要設計
一: 查詢優化階段
-
實現 IsForeignScanParallelSafe ,返回 true。 標誌該數據源可以並行化。
- 補充函數 GetForeignPaths 根據外數據的規模和可提供的並行工作進程數等信息提供給優化器可以行並行 Path。
- 調用 create_foreignscan_path 創建可並行的外部表掃描節點 Path。
- 調用 add_partial_path 把生成好的 Path 加入優化器 Path 備選隊列。
- 補充函數 GetForeignPlan 創建可並行的外部表掃描節點 Plan。
- 函數內部調用 make_foreignscan 根據輸入的 Path 生成 Plan, 並向上返回。
二: 查詢執行階段
並行任務關鍵當然是把一個大的任務拆分成多個盡可能不相關的子任務,讓這些子任務被並行的完成。
例如:
-
1 對外部 MySQL 一張表 t 的讀取,可以按照表 t 主鍵的值域把數據拆分成 N 部分,讓並行 worker 分別讀取其中一部分。
-
2 對外部數據源 oss 一個目錄 dir1 中多個文件中數據的讀取,可以把這批文件均勻的分成 N 份,讓並行 worker 分別完成其中的一部分。
如何合理的切分子任務,往往決定了最終的並行效果。合理的切分數據會使並行任務間盡量少的交互,最終任務完成耗時和並行工作進程數線性相關。
執行器的具體工作:
-
實現 EstimateDSMForeignScan 計算需求的共享內存大小。這部分內存將用戶存放整個並行任務的相關信息。
這部分流程主進程完成,即 Gather 節點完成。 -
實現 InitializeDSMForeignScan 分配共享內存,放入相關信息。
我們把整個大任務拆分成一個子任務隊列,並存入到共享內存中,初始化鎖等信息。
這部分流程也主進程完成,即 Gather 節點完成。 -
實現 InitializeWorkerForeignScan 並行 Worker 讀取共享內存上的信息,獲取子任務,準備正式開始工作。
-
數據的讀寫操作。
這部分的實現盡量兼容傳統模式的數據讀取,或小幅調整。 -
實現 ShutdownForeignScan 數據掃描完成的後清理工作。
詳細的 Foreign Data Wrapper 接口實現說明在這
並行外部表查詢的應用
並行查詢能大大提高數據的訪問效率,他把外部數據源深度整合到 PostgreSQL 中。可以輕鬆的和本地數據一起做複雜的運算。同時,我們也能利用這套機製,實現高效的外部數據導入工作。
最後更新:2017-05-21 09:01:51