云原生數據倉庫AnalyticDB PostgreSQL版支持靜態分區和動態分區裁剪。在掃描分區表前,能通過檢查分區約束條件與每個分區的定義,提前排除不需要掃描的分區,大大減少從磁盤中讀取的數據量,從而縮短運行時間,改善查詢性能,減少資源浪費。
云原生數據倉庫AnalyticDB PostgreSQL版支持按列分區。分區表能夠將大的事實表分解成多個小表,根據查詢條件自動跳過不滿足條件的分區數據,從而提高查詢效率。分區裁剪(Partition Pruning)是數據庫分區表中常用的性能優化手段。
使用限制
僅支持分區表。
僅支持在Range或List分區列上使用范圍、等式和IN列表謂詞。AnalyticDB PostgreSQL 7.0版支持Hash分區,Hash分區只能通過等值條件進行裁剪。
動態分區裁剪僅支持分區列的等值條件,如
=
或IN
等。分區裁剪效果與數據分布有關,如果分區約束無法有效裁剪,性能會回退到與全表掃描一致。
靜態分區裁剪
概述
如果分區約束為確定的表達式,在查詢規劃階段就可以根據分區約束表達式裁掉不需要掃描的分區,這種在查詢規劃階段做分區裁剪的方式稱為靜態分區裁剪。
AnalyticDB PostgreSQL版主要通過靜態謂詞確定何時使用靜態裁剪。支持的靜態謂詞包括:=
、>
、>=
、<
、<=
五種操作符,以及IN
列表。
靜態分區裁剪可以從EXPLAIN輸出查看裁剪結果。
示例
示例一:通過靜態謂詞
=
進行分區裁剪。--創建分區表。 CREATE TABLE sales (id int, year int, month int, day int, region text) DISTRIBUTED BY (id) PARTITION BY RANGE (month) SUBPARTITION BY LIST (region) SUBPARTITION TEMPLATE ( SUBPARTITION usa VALUES ('usa'), SUBPARTITION europe VALUES ('europe'), SUBPARTITION asia VALUES ('asia'), DEFAULT SUBPARTITION other_regions) (START (1) END (13) EVERY (1), DEFAULT PARTITION other_months ); --分區裁剪。 EXPLAIN SELECT * FROM sales WHERE year = 2008 AND month = 1 AND day = 3 AND region = 'usa';
由于查詢條件落在一級分區1的二級子分區
'usa'
上,查詢只會掃描讀取這個二級子分區數據。如下其查詢計劃顯示,總計52個三級子分區中,只有一個分區被讀取(Partitions selected)。Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=24) -> Sequence (cost=0.00..431.00 rows=1 width=24) -> Partition Selector for sales (dynamic scan id: 1) (cost=10.00..100.00 rows=34 width=4) Partitions selected: 1 (out of 52) -> Dynamic Seq Scan on sales (dynamic scan id: 1) (cost=0.00..431.00 rows=1 width=24) Filter: ((year = 2008) AND (month = 1) AND (day = 3) AND (region = 'usa'::text))
示例二:通過靜態謂詞
>=
及IN
列表進行分區裁剪。EXPLAIN SELECT * FROM sales WHERE month in (1,5) AND region >= 'usa'; QUERY PLAN ----------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=24) -> Sequence (cost=0.00..431.00 rows=1 width=24) -> Partition Selector for sales (dynamic scan id: 1) (cost=10.00..100.00 rows=34 width=4) Partitions selected: 6 (out of 52) -> Dynamic Seq Scan on sales (dynamic scan id: 1) (cost=0.00..431.00 rows=1 width=24) Filter: ((month = ANY ('{1,5}'::integer[])) AND (region >= 'usa'::text))
靜態分區裁剪不支持LIKE
和<>
操作符。例如,將示例二的查詢,WHERE條件改成region LIKE 'usa'
,就無法進行分區裁剪。
EXPLAIN
SELECT * FROM sales
WHERE region LIKE 'usa';
QUERY PLAN
-----------------------------------------------------------------------------------------------------
Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=24)
-> Sequence (cost=0.00..431.00 rows=1 width=24)
-> Partition Selector for sales (dynamic scan id: 1) (cost=10.00..100.00 rows=34 width=4)
Partitions selected: 52 (out of 52)
-> Dynamic Seq Scan on sales (dynamic scan id: 1) (cost=0.00..431.00 rows=1 width=24)
Filter: (region ~~ 'usa'::text)
動態分區裁剪
概述
在PREPARE-EXECUTE執行方式和分區約束表達式中包含子查詢的場景中,查詢規劃階段無法確定分區約束,只能在執行階段,通過外部參數和子查詢的結果進行裁剪,這種在執行階段做分區裁剪的方式稱為動態分區裁剪。
動態分區裁剪可以通過EXPLAIN ANALYZE從執行結果中查看裁剪結果。
動態分區裁剪JOIN優化
AnalyticDB PostgreSQL版針對數倉場景下常見的事實表和維度表JOIN,可以將事實表進行分區,通過動態分區裁剪進行JOIN優化。
一般來說,事實表很大,而維度表很小。如果JOIN KEY為事實表的分區鍵,那么AnalyticDB PostgreSQL版會根據小表的數據,動態生成大表的分區約束,這有助于跳過一些分區文件,從而減少JOIN算子的數據量。
動態分區裁剪的原理:利用JOIN算子內表的數據,動態生成外表(分區表)的分區過濾器,從而跳過不需要的分區。如果沒有分區裁剪,分區大表和小表的JOIN可以簡化為下圖:
當JOIN KEY為分區鍵,那么分區表中的所有分區都需要被掃描,然后和小表進行JOIN。而動態分區裁剪會先掃描小表,生成一個分區過濾器,并傳給大表的Scan算子,這樣只有一部分分區被提取到JOIN算子中,大大減少了數據量。有分區裁剪,分區大表和小表的JOIN簡化如下圖:
因此,對于大表JOIN小表的典型場景,可以考慮通過將大表改造成分區表,利用動態分區裁剪的特性加速查詢。
示例
示例一:WHERE條件包含子查詢的動態分區裁剪。
CREATE TABLE t1 (a int, b int); INSERT INTO t1 VALUES (3,3), (5,5); EXPLAIN ANALYZE SELECT * FROM sales WHERE month = ( SELECT MIN(a) FROM t1 );
WHERE條件包含子查詢,子查詢的結果在查詢規劃階段未知,需要在執行階段確定分區約束。根據插入的數據來看,
MIN(a)
應該為3,所以sales
表應該只讀取month=3
的4個子分區(Partitions scanned: Avg 4.0)。Gather Motion 3:1 (slice3; segments: 3) (cost=0.00..862.00 rows=1 width=24) (actual time=5.134..5.134 rows=0 loops=1) -> Hash Join (cost=0.00..862.00 rows=1 width=24) (never executed) Hash Cond: (sales.month = (min((min(t1.a))))) -> Sequence (cost=0.00..431.00 rows=1 width=24) (never executed) -> Partition Selector for sales (dynamic scan id: 1) (cost=10.00..100.00 rows=34 width=4) (never executed) Partitions selected: 52 (out of 52) -> Dynamic Seq Scan on sales (dynamic scan id: 1) (cost=0.00..431.00 rows=1 width=24) (never executed) Partitions scanned: Avg 4.0 (out of 52) x 3 workers. Max 4 parts (seg0). -> Hash (cost=100.00..100.00 rows=34 width=4) (actual time=0.821..0.821 rows=1 loops=1) -> Partition Selector for sales (dynamic scan id: 1) (cost=10.00..100.00 rows=34 width=4) (actual time=0.817..0.817 rows=1 loops=1) -> Broadcast Motion 1:3 (slice2) (cost=0.00..431.00 rows=3 width=4) (actual time=0.612..0.612 rows=1 loops=1) -> Aggregate (cost=0.00..431.00 rows=1 width=4) (actual time=1.204..1.205 rows=1 loops=1) -> Gather Motion 3:1 (slice1; segments: 3) (cost=0.00..431.00 rows=1 width=4) (actual time=1.047..1.196 rows=3 loops=1) -> Aggregate (cost=0.00..431.00 rows=1 width=4) (actual time=0.012..0.012 rows=1 loops=1) -> Seq Scan on t1 (cost=0.00..431.00 rows=1 width=4) (actual time=0.005..0.005 rows=2 loops=1)
示例二:通過動態分區裁剪進行JOIN優化。
EXPLAIN SELECT * FROM sales JOIN t1 ON sales.month = t1.a WHERE sales.region = 'usa';
t1
表中只有兩條記錄(3,3)
和(5,5)
,與sales
表的分區鍵month
進行JOIN后,動態分區裁剪應該只掃描month
為3和5的分區,再加上region
指定為'usa'
,最終只有兩個子分區被掃描(Avg 2.0)。通過EXPLAIN ANALYZE能得到其運行時的執行計劃,符合預期。QUERY PLAN --------------------------------------------------------------------------------------------------------------- Gather Motion 3:1 (slice2; segments: 3) (actual time=3.204..16.022 rows=6144 loops=1) -> Hash Join (actual time=2.212..11.938 rows=6144 loops=1) Hash Cond: (sales.month = t1.a) Extra Text: (seg1) Hash chain length 1.0 avg, 1 max, using 2 of 524288 buckets. -> Sequence (actual time=0.317..4.197 rows=6144 loops=1) -> Partition Selector for sales (dynamic scan id: 1) (never executed) Partitions selected: 13 (out of 52) -> Dynamic Seq Scan on sales (dynamic scan id: 1) (actual time=0.311..3.391 rows=6144 loops=1) Filter: (region = 'usa'::text) Partitions scanned: Avg 2.0 (out of 52) x 3 workers. Max 2 parts (seg0). -> Hash (actual time=0.316..0.316 rows=2 loops=1) -> Partition Selector for sales (dynamic scan id: 1) (actual time=0.208..0.310 rows=2 loops=1) -> Broadcast Motion 3:3 (slice1; segments: 3) (actual time=0.008..0.012 rows=2 loops=1) -> Seq Scan on t1 (actual time=0.004..0.004 rows=1 loops=1)
常見問題
Q:如何判斷我的查詢進行了分區裁剪?
A:可以通過EXPLAIN語句觀察執行計劃,如果出現
Partition Selector
的算子,就說明分區裁剪生效。Q:分區裁剪在ORCA和PG原生優化器上都支持嗎?
A:是的,Planner和ORCA都支持動態或靜態分區裁剪,只是執行計劃稍有區別。
Q:為什么分區裁剪不生效?
A:分區裁剪需要對分區鍵進行FILTER或者JOIN。對于靜態分區裁剪,目前僅支持
=
、>
、>=
、<
、<=
五種操作符,以及IN列表;對于動態分區裁剪,目前僅支持等值條件。