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的只讀節點。默認為空,表示所有只讀節點都參與??膳渲脼橹付ü濣c參與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%的索引創建性能。