PolarDB PostgreSQL版實現了彈性跨機並行查詢(ePQ)特性,能夠協助您解決原先的PolarDB PostgreSQL版在處理複雜的AP查詢時會遇到的問題。
前提條件
支援的PolarDB PostgreSQL版的版本如下:
PostgreSQL 11(核心小版本1.1.28及以上)
您可通過如下語句查看PolarDB PostgreSQL版的核心小版本的版本號碼:
show polar_version;背景資訊
很多PolarDB PostgreSQL版的使用者都有TP(Transactional Processing)和AP(Analytical Processing)共用的需求。他們期望資料庫在白天處理高並發的TP請求,在夜間TP流量下降、機器負載空閑時進行AP的報表分析。但是即使這樣,依然沒有最大化利用空閑機器的資源。原先的PolarDB PostgreSQL版在處理複雜的AP查詢時會遇到兩大挑戰:
單條SQL在原生PostgreSQL執行引擎下只能在單個節點上執行,無論是單機串列還是單機並行,都無法利用其他節點的CPU、記憶體等計算資源,只能縱向Scale Up,不能橫向Scale Out。
PolarDB PostgreSQL版底層是儲存池,理論上I/O吞吐是無限大的。而單條SQL在原生PostgreSQL執行引擎下只能在單個節點上執行,受限於單節點CPU和記憶體的瓶頸,無法充分發揮儲存側大I/O頻寬的優勢。
如圖所示:
為瞭解決使用者實際使用中的痛點,PolarDB PostgreSQL版實現了ePQ特性。當前業界HTAP的解決方案主要有以下三種:
TP和AP在儲存和計算上完全分離。
優勢:兩種業務負載互不影響。
劣勢:
時效性:TP的資料需要匯入到AP系統中,存在一定的延遲。
成本/營運難度:增加了一套冗餘的AP系統。
TP和AP在儲存和計算上完全共用。
優勢:成本最小化、資源利用最大化。
劣勢:
計算共用會導致AP查詢和TP查詢同時運行時會存在相互影響。
擴充計算節點儲存時,資料需要重分布,無法快速彈性Scale Out。
TP和AP在儲存上共用,在計算上分離。
說明本身的儲存計算分離架構天然支援此方案。
HTAP特性原理
架構特性。
基於PolarDB PostgreSQL版的儲存計算分離架構,推出了分布式ePQ執行引擎,提供了跨機並存執行、彈性計算、彈性擴充的保證,使得PolarDB PostgreSQL版初步具備了HTAP的能力。其優勢如下:
一體化儲存:毫秒級資料新鮮度。
TP/AP共用一套儲存資料,減少儲存成本,提高查詢時效。
TP/AP物理隔離:杜絕CPU/記憶體的相互影響。
單機執行引擎:在RW/RO節點,處理高並發的TP查詢。
分布式ePQ執行引擎:在RO節點,處理高複雜度的AP查詢。
Serverless彈性擴充:任何一個RO節點均可發起ePQ查詢。
Scale Out:彈性調整ePQ的執行節點範圍。
Scale Up:彈性調整ePQ的單機並行度。
消除資料扭曲、計算傾斜,充分考慮PolarDB PostgreSQL版的Buffer Pool親和性。
如圖所示:

分布式ePQ執行引擎。
PolarDB PostgreSQL版ePQ的核心是分布式ePQ執行引擎,是典型的火山模型引擎。A、B兩張表先做join再做彙總輸出,這也是PostgreSQL單機執行引擎的執行流程。執行流程如下所示:

在傳統的mpp執行引擎中,資料被打散到不同的節點上,不同節點上的資料可能具有不同的分布屬性,例如雜湊分布、隨機分布、複製分布等。傳統的mpp執行引擎會針對不同表的資料分布特點,在執行計畫中插入運算元來保證上層運算元對資料的分布屬性無感知。
不同的是,PolarDB PostgreSQL版是共用儲存架構,儲存上的資料可以被所有計算節點全量訪問。如果使用傳統的mpp執行引擎,每個計算節點Worker都會掃描全量資料,從而得到重複的資料。同時,也沒有起到掃描時分治加速的效果,並不能稱得上是真正意義上的mpp引擎。
因此,在分布式ePQ執行引擎中,我們借鑒了火山模型論文中的思想,對所有掃描運算元進行並發處理,引入了PxScan運算元來屏蔽共用儲存。PxScan運算元將shared-storage的資料對應為shared-nothing的資料,通過Worker之間的協調,將目標表劃分為多個虛擬分區資料區塊,每個Worker掃描各自的虛擬分區資料區塊,從而實現了跨機分布式並行掃描。
PxScan運算元掃描出來的資料會通過Shuffle運算元來重分布。重分布後的資料在每個Worker上如同單機執行一樣,按照火山模型來執行。
Serverless彈性擴充。
傳統mpp只能在指定節點發起mpp查詢,因此每個節點上都只能有單個Worker掃描一張表。為了支援雲原生下Serverless彈性擴充的需求,我們引入了分散式交易一致性保障。如圖所示:

任意選擇一個節點作為Coordinator節點,它的ReadLSN會作為約定的LSN,從所有ePQ節點的快照版本號碼中選擇最小的版本號碼作為全域約定的快照版本號碼。通過LSN的回放等待和Global Snapshot同步機制,確保在任何一個節點發起ePQ查詢時,資料和快照均能達到一致可用的狀態。如圖所示:

為了實現Serverless的彈性擴充,PolarDB PostgreSQL版從共用儲存的特點出發,將Coordinator節點全鏈路上各個模組需要的外部依賴全部放至共用儲存上。各個Worker節點運行時需要的參數也會通過控制鏈路從Coordinator節點同步過來,從而使Coordinator節點和Worker節點全鏈路無狀態化(Stateless)。
基於以上兩點設計,PolarDB PostgreSQL版的彈性擴充具備了以下幾大優勢:
任何節點都可以成為Coordinator節點,解決了傳統mpp資料庫Coordinator節點的單點問題。
PolarDB PostgreSQL版可以橫向Scale Out(計算節點數量),也可以縱向Scale Up(單節點並行度),且彈性擴充即時生效,不需要重新分配資料。
允許業務有更多的彈性調度策略,不同的業務域可以運行在不同的節點集合上。業務域1的SQL可以選擇RO1和RO2節點來執行AP查詢,業務域2的SQL可以選擇使用RO3和RO4節點來執行AP查詢,兩個業務域使用的計算節點可以實現彈性調度。

消除傾斜。
傾斜是傳統mpp固有的問題,其根本原因主要是資料分布傾斜和資料計算傾斜。
資料分布傾斜通常由資料打散不均衡導致,在PostgreSQL中還會由於大對象Toast表格儲存體引入一些不可避免的資料分布不均衡問題。
計算傾斜通常由於不同節點上並發的事務、Buffer Pool、網路、I/O抖動導致。
傾斜會導致傳統mpp在執行時出現木桶效應,執行完成時間受制於執行最慢的子任務。
PolarDB PostgreSQL版設計並實現了自適應掃描機制。如下圖所示,採用Coordinator節點來協調Worker節點的工作模式。在掃描資料時,Coordinator節點會在記憶體中建立一個工作管理員,根據掃描任務對Worker節點進行調度。Coordinator節點內部分為兩個線程。
Data線程主要負責服務資料鏈路、收集匯總元組。
Control線程負責服務控制鏈路、控制每一個掃描運算元的掃描進度。

掃描進度較快的Worker能夠掃描多個資料區塊。如上圖中RO1與RO3的Worker各自掃描了4個資料區塊, RO2由於計算傾斜可以掃描更多資料區塊,因此它最終掃描了6個資料區塊。
PolarDB PostgreSQL版ePQ的自適應掃描機制還充分考慮了PostgreSQL的Buffer Pool親和性,保證每個Worker儘可能掃描固定的資料區塊,從而最大化命中Buffer Pool的機率,降低I/O開銷。
TPC-H效能對比
單機並行與分布式ePQ對比。
使用256 GB記憶體的16個PolarDB PostgreSQL版叢集作為RO節點,搭建1 TB的TPC-H環境進行對比測試。相較於單機並行,分布式ePQ並行充分利用了所有RO節點的計算資源和底層共用儲存的I/O頻寬,從根本上解決了前文提及的HTAP諸多挑戰。在TPC-H的22條SQL中,有3條SQL加速了60多倍,19條SQL加速了10多倍,平均加速23倍。如圖所示:

測試彈性擴充計算資源帶來的效能變化。通過增加CPU的總核心數,從16核增加到128核,TPC-H的總已耗用時間線性提升,每條SQL的執行速度也呈線性提升,這也驗證了PolarDB PostgreSQL版ePQ Serverless彈性擴充的特點。如圖所示:

在測試中發現,當CPU的總核心數增加到256核時,效能提升不再明顯。原因是此時PolarDB PostgreSQL版共用儲存的I/O頻寬已經達到100%,成為了瓶頸。
PolarDB與傳統mpp資料庫對比。
同樣使用256 GB記憶體的16個節點,將PolarDB PostgreSQL版的分布式ePQ執行引擎與傳統資料庫的mpp執行引擎進行對比。
在1 TB的TPC-H資料上,當保持與傳統mpp資料庫相同單機並行度的情況下(多機單進程),PolarDB PostgreSQL版的效能是傳統mpp資料庫的90%。其中最本質的原因是傳統mpp資料庫的資料預設是雜湊分布的,當兩張表的join key是各自的分布鍵時,可以不用shuffle直接進行本地的Wise Join。而PolarDB PostgreSQL版的底層是共用儲存池,PxScan運算元並行掃描出來的資料等價於隨機分布,必須進行shuffle重分布以後才能像傳統mpp資料庫一樣進行後續的處理。因此,TPC-H涉及到表串連時,PolarDB PostgreSQL版相比傳統mpp資料庫多了一次網路shuffle的開銷。如圖所示:


PolarDB PostgreSQL版分布式ePQ執行引擎能夠進行彈性擴充,資料無需重分布。因此,在有限的16台機器上執行ePQ時,PolarDB PostgreSQL版還可以繼續擴充單機並行度,充分利用每台機器的資源。當PolarDB PostgreSQL版的單機並行度為8時,它的效能是傳統mpp資料庫的5-6倍;當PolarDB PostgreSQL版的單機並行度呈線性增加時,PolarDB PostgreSQL版的總體效能也呈線性增加。只需要修改配置參數,就可以即時生效。
功能特性
Parallel Query並行查詢。
經過持續迭代的研發,目前PolarDB PostgreSQL版ePQ在Parallel Query上支援的功能特性主要有五大部分:
基礎運算元全支援:掃描/串連/彙總/子查詢等運算元。
共用儲存運算元最佳化:包括Shuffle運算元共用、SharedSeqScan共用、SharedIndexScan運算元等。其中SharedSeqScan共用、SharedIndexScan共用是指,在大表join小表時,小表採用類似於複製表的機制來減少廣播開銷,進而提升效能。
分區表支援:不僅包括對Hash/Range/List三種分區方式的完整支援,還包括對多級分區靜態裁剪、分區動態裁剪的支援。除此之外,PolarDB PostgreSQL版分布式ePQ執行引擎還支援分區表的Partition Wise Join。
並行度彈性控制:包括全域層級、表層級、會話層級、查詢層級的並行度控制。
Serverless彈性擴充:不僅包括任意節點發起ePQ、ePQ節點範圍內的任意組合,還包括叢集拓撲資訊的自動維護以及支援共用儲存模式、主備庫模式和三節點模式。
Parallel DML。
基於PolarDB PostgreSQL版讀寫分離架構和ePQ Serverless彈性擴充的設計, PolarDB PostgreSQL版Parallel DML支援一寫多讀、多寫多讀兩種特性。
一寫多讀:在RO節點上有多個讀Worker,在RW節點上只有一個寫Worker。
多寫多讀:在RO節點上有多個讀Worker,在RW節點上也有多個寫Worker。多寫多讀情境下,讀寫的並發度完全解耦。
不同的特性適用不同的情境,使用者可以根據自己的業務特點來選擇不同的Parallel DML功能特性。
索引構建加速。
PolarDB PostgreSQL版分布式ePQ執行引擎,不僅可以用於唯讀查詢和DML,還可以用於索引構建加速。OLTP業務中有大量的索引,而B-Tree索引建立的過程大約有80%的時間消耗在排序和構建索引頁上,20%消耗在寫入索引頁上。如下圖所示:
PolarDB PostgreSQL版利用RO節點對資料進行分布式ePQ加速排序,採用流水化的技術來構建索引頁,同時使用批量寫入技術來提升索引頁的寫入速度。說明在目前索引構建加速這一特性中,PolarDB PostgreSQL版已經支援了B-Tree索引的普通建立以及B-Tree索引的線上建立(Concurrently)兩種功能。
使用指南
PolarDB PostgreSQL版ePQ適用於日常業務中的輕分析類業務,例如:對賬業務,報表業務。
使用ePQ進行分析型查詢。
PolarDB PostgreSQL版預設不開啟ePQ功能。若您需要使用此功能,請使用如下參數:
參數
說明
polar_enable_px
指定是否開啟ePQ功能。
on:開啟ePQ功能。
off:(預設值)不開啟ePQ功能。
polar_px_max_workers_number
設定單個節點上的最大ePQ Worker進程數,預設為30,取值範圍為0~2147483647。該參數限制了單個節點上的最大並行度。
說明節點上所有會話的ePQ workers進程數不能超過該參數大小。
polar_px_dop_per_node
設定當前會話並行查詢的並行度,預設為1,推薦值為當前CPU 總核心數,取值範圍為1~128。若設定該參數為N,則一個會話在每個節點上將會啟用N個ePQ Worker進程,用於處理當前的ePQ邏輯。
polar_px_nodes
指定參與ePQ的唯讀節點。預設為空白,表示所有隻讀節點都參與。可配置為指定節點參與ePQ,以逗號分隔。
px_worker
指定ePQ是否對特定表生效。預設不生效。ePQ功能比較消耗叢集計算節點的資源,因此只有對設定了px_workers的表才使用該功能。例如:
ALTER TABLE t1 SET(px_workers=1):表示t1表允許ePQ。
ALTER TABLE t1 SET(px_workers=-1):表示t1表禁止ePQ。
ALTER TABLE t1 SET(px_workers=0):表示t1表忽略ePQ(預設狀態)。
以簡單的單表查詢操作,來描述ePQ的功能是否有效。
建立test表並插入基礎資料。
CREATE TABLE test(id int); INSERT INTO test SELECT generate_series(1,1000000);查詢執行計畫。
EXPLAIN SELECT * FROM test;執行結果如下:
QUERY PLAN -------------------------------------------------------- Seq Scan on test (cost=0.00..35.50 rows=2550 width=4) (1 row)說明預設情況下ePQ功能不開啟,單表查詢執行計畫為PG原生的Seq Scan。
開啟並使用ePQ功能。
ALTER TABLE test SET (px_workers=1); SET polar_enable_px = on; EXPLAIN SELECT * FROM test;結果如下:
QUERY PLAN ------------------------------------------------------------------------------- PX Coordinator 2:1 (slice1; segments: 2) (cost=0.00..431.00 rows=1 width=4) -> Seq Scan on test (scan partial) (cost=0.00..431.00 rows=1 width=4) Optimizer: PolarDB PX Optimizer (3 rows)配置參與ePQ的計算節點範圍。
查詢當前所有隻讀節點的名稱。
CREATE EXTENSION polar_monitor; SELECT name,host,port FROM polar_cluster_info WHERE px_node='t';結果如下:
name | host | port -------+-----------+------ node1 | 127.0.0.1 | 5433 node2 | 127.0.0.1 | 5434 (2 rows)說明當前叢集有2個唯讀節點,名稱分別為:node1,node2。
指定node1隻讀節點參與ePQ。
SET polar_px_nodes = 'node1';查詢參與並行查詢的節點。
SHOW polar_px_nodes;結果如下:
polar_px_nodes ---------------- node1 (1 row)說明參與並行查詢的節點是node1。
查詢test表中所有資料。
EXPLAIN SELECT * FROM test;結果如下:
QUERY PLAN ------------------------------------------------------------------------------- PX Coordinator 1:1 (slice1; segments: 1) (cost=0.00..431.00 rows=1 width=4) -> Partial Seq Scan on test (cost=0.00..431.00 rows=1 width=4) Optimizer: PolarDB PX Optimizer (3 rows) QUERY PLAN
使用ePQ進行分區表查詢。
開啟ePQ功能。
SET polar_enable_px = ON;開啟分區表ePQ功能。
SET polar_px_enable_partition = true;說明分區表ePQ功能預設關閉。
開啟多級分區表ePQ功能。
SET polar_px_optimizer_multilevel_partitioning = true;
當前ePQ對分區表支援的功能如下所示:
支援Range分區的並行查詢。
支援List分區的並行查詢。
支援單列Hash分區的並行查詢。
支援分區裁剪。
支援帶有索引的分區表並行查詢。
支援分區表串連查詢。
支援多級分區的並行查詢。
使用ePQ加速索引建立。
開啟使用ePQ加速建立索引功能。
SET polar_px_enable_btbuild = on;建立索引。
CREATE INDEX t ON test(id) WITH(px_build = ON);查詢表結構。
\d test結果如下:
Table "public.test" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- id | integer | | | id2 | integer | | | Indexes: "t" btree (id) WITH (px_build=finish)
說明當前僅支援對B-Tree索引的構建,且暫不支援INCLUDE等索引構建文法,暫不支援運算式等索引列類型。
如果需要使用ePQ功能加速建立索引,請使用如下參數:
參數
說明
polar_px_dop_per_node
指定通過ePQ加速構建索引的並行度。預設為1,取值範圍為1~128。
polar_px_enable_replay_wait
當使用ePQ加速索引構建時,當前會話內無需手動開啟該參數,該參數將自動生效。以保證最新動向的資料表項可以被建立到索引中,保證索引表的完整性。索引建立完成後,該參數將會被重設為資料庫預設值。
polar_px_enable_btbuild
是否開啟使用ePQ加速建立索引。取值為OFF時不開啟(預設),取值為ON時開啟。
polar_bt_write_page_buffer_size
指定索引構建過程中的寫I/O策略。該參數預設值為0(不開啟),單位為塊,最大值可設定為8192。推薦設定為4096。
參數值為0:該參數設定為不開啟,在索引建立的過程中,對於索引頁寫滿後的寫盤方式是block-by-block的單個塊寫盤。
參數值不為0:該參數設定為開啟,核心中將緩衝一個polar_bt_write_page_buffer_size大小的buffer,對於需要寫盤的索引頁,會通過該buffer進行I/O合并再統一寫盤,避免了頻繁調度I/O帶來的效能開銷。該參數會額外提升20%的索引建立效能。