PolarDB PostgreSQL版(相容Oracle)支援分區剪枝功能,可以顯著提高對分區表的查詢速度。
概述
PolarDB PostgreSQL版(相容Oracle)提供了分區剪枝(Partition Pruning)功能,如果啟用了分區剪枝,規劃器將會檢查每個分區的定義並且檢驗該分區是否因為不包含符合查詢WHERE子句的行而無需掃描。若不包含,則會把分區從查詢計劃中排除(剪枝)。分區剪枝極大地減少了從磁碟檢索的資料量並縮短了處理時間,從而提高了查詢效能並最佳化了資源使用率。
根據實際的SQL語句,PolarDB PostgreSQL版(相容Oracle)資料庫支援使用靜態或動態剪枝。
靜態剪枝發生在編譯時間,並預先訪問有關分區的資訊。靜態剪枝的一個樣本情境是包含WHERE條件的SQL語句,該條件在分區鍵列上帶有常量文字。
動態剪枝發生在運行時,事先不知道語句要訪問的確切分區。動態剪枝的一個樣本是在WHERE條件中使用運算子或函數。
分區剪枝會影響發生剪枝的對象的統計資訊,也影響語句的執行計畫。
分區剪枝技術將資料搜尋限制為僅搜尋您要搜尋的值可能所在的分區。 這兩種剪枝技術都會從查詢的執行計畫中刪除分區,從而提高效能。
分區剪枝無法最佳化對子分區表的查詢,也無法最佳化分區於多個列上的RANGE分區表的查詢。
和約束排除類似,分區剪枝只能對包含WHERE子句的查詢進行最佳化,且只有當WHERE子句中的限定符滿足某種格式時,才可進行最佳化。
參數
參數名稱 | 描述 |
polar_comp_enable_pruning | 用於控制分區剪枝,取值範圍如下:
|
使用說明
按分區類型
LIST分區表查詢
對於LIST分區表的查詢,分區剪枝能快速剪去包含WHERE子句的查詢,運算子為等於(=)或IS NULL/IS NOT NULL。
樣本:
------指定一個LIST分區表 CREATE TABLE sales_hist(..., country text, ...) PARTITION BY LIST(country) ( PARTITION americas VALUES('US', 'CA', 'MX'), PARTITION europe VALUES('BE', 'NL', 'FR'), PARTITION asia VALUES('JP', 'PK', 'CN'), PARTITION others VALUES(DEFAULT) ) ------在該LIST分區表的基礎上,提取出包含如下WHERE子句的資訊 WHERE country = 'US' WHERE country IS NULL;通過第一個WHERE子句, 分區剪枝將排除分區europe、asia和others,因為這些分區不滿足
WHERE country = 'US'。通過第二個WHERE子句, 分區剪枝將排除分區americas、europe和asia。因為這些分區不滿足
WHERE country IS NULL。
RANGE分區表查詢
對於RANGE分區表的查詢,分區剪枝能快速剪去包含WHERE子句的查詢。運算子可以是等於(=)、大於(>)、大於等於(>=)、小於(<)、小於等於(<=),或者是IS NULL/IS NOT NULL,也可以是更為複雜的包含運算子AND和 BETWEEN的運算式,例如:
WHERE size > 100 AND size <= 200 WHERE size BETWEEN 100 AND 200說明分區剪枝不支援包含OR或IN運算子的運算式。
樣本:
------指定一個RANGE分區表 CREATE TABLE boxes(id int, size int, color text) PARTITION BY RANGE(size) ( PARTITION small VALUES LESS THAN(100), PARTITION medium VALUES LESS THAN(200), PARTITION large VALUES LESS THAN(300) ) ------在該RANGE分區表的基礎上,提取出包含如下WHERE子句的資訊。 WHERE size > 100 -- 掃描medium和large分區 WHERE size >= 100 -- 掃描medium和large分區 WHERE size = 100 -- 掃描medium分區 WHERE size <= 100 -- 掃描small和medium分區 WHERE size < 100 -- 掃描small分區 WHERE size > 100 AND size < 199 -- 掃描medium分區 WHERE size BETWEEN 100 AND 199 -- 掃描medium分區 WHERE color = 'red' AND size = 100 -- 掃描medium分區 WHERE color = 'red' AND (size > 100 AND size < 199) -- 掃描medium分區
按不同階段
PolarDB PostgreSQL版(相容Oracle)中將條件運算式分為三個層級,即不變的(Immutable)、穩定的(Stable)、易變的(Volatile)。這三個層級依次對應了三種剪枝:
如果條件運算式值是不變的(比如常量靜態值),則它會被用於最早的最佳化器剪枝;
如果條件運算式值是穩定的(比如
now()),則會發生執行器初始階段剪枝;如果條件運算式是易變的(比如
random()),則會發生執行器運行時剪枝。
最佳化器剪枝
可以通過如下樣本來瞭解最佳化器階段的剪枝。
在PolarDB PostgreSQL版(相容Oracle)中建立一個measurement表,logdate作為分區鍵,然後為其分別建立4個分區,即measurement_y2023q1,measurement_y2023q2, measurement_y2023q3, measurement_y2023q4四個分區,分別對應了2023年的四個季度。
CREATE TABLE measurement(
city_id int not null,
logdate date not null,
peaktemp int,
unitsales int
) PARTITION BY RANGE (logdate);
CREATE TABLE measurement_y2023q1 PARTITION OF measurement
FOR VALUES FROM ('2023-01-01') TO ('2023-04-01');
CREATE TABLE measurement_y2023q2 PARTITION OF measurement
FOR VALUES FROM ('2023-04-01') TO ('2023-07-01');
CREATE TABLE measurement_y2023q3 PARTITION OF measurement
FOR VALUES FROM ('2023-07-01') TO ('2023-10-01');
CREATE TABLE measurement_y2023q4 PARTITION OF measurement
FOR VALUES FROM ('2023-10-01') TO ('2024-04-01');此時查詢logdate大於2023-10-01的所有資料。使用EXPLAIN可以看到,第一、二、三季度的資料預設都被剪枝掉了,不會執行查詢,因為前三個分區的範圍明顯不滿足logdate >= DATE '2023-10-01'。這就是最佳化器階段的分區剪枝,可以看到SQL中的限定條件是分區鍵logdate,且條件運算式的值DATE '2023-10-01'是靜態,或者是不可變的,它可以在最佳化器階段計算出來。
EXPLAIN SELECT * FROM measurement WHERE logdate >= DATE '2023-10-01';
QUERY PLAN
-----------------------------------------------------------------------------
Append (cost=0.00..34.09 rows=567 width=20)
-> Seq Scan on measurement_y2023q4 (cost=0.00..31.25 rows=567 width=20)
Filter: (logdate >= '01-OCT-23 00:00:00'::date)
(3 rows)執行器初始階段剪枝
同樣通過上文measurement表來瞭解執行器初始剪枝。
如下可以看到,同樣的measurement表,同樣的SQL查詢,但是查詢條件的運算式值從靜態值變成了now(),這是一個穩定的運算式,它不能在最佳化器階段計算,但是可以在執行器初始階段計算。假設今天是2023年7月,因此可以看到前兩個季度分區被移除了Subplans Removed: 2,只剩下了第三季度和第四季度。這就是執行器初始階段的分區剪枝,可以看到SQL中的限定條件是分區鍵logdate,且條件運算式的值now()是穩定的,它可以在執行器初始階段計算出來。
EXPLAIN SELECT * FROM measurement WHERE logdate >= now();
QUERY PLAN
-----------------------------------------------------------------------------
Append (cost=0.00..153.34 rows=2268 width=20)
Subplans Removed: 2
-> Seq Scan on measurement_y2023q3 (cost=0.00..35.50 rows=567 width=20)
Filter: (logdate >= now())
-> Seq Scan on measurement_y2023q4 (cost=0.00..35.50 rows=567 width=20)
Filter: (logdate >= now())
(6 rows)
執行器運行時剪枝
同樣通過上文measurement表來瞭解執行器運行時剪枝。
如下可以看到,同樣的measurement表,同樣的SQL查詢,但是查詢條件的運算式值從靜態值變成了(select to_date('2023-10-1')),這是一個易變的子串連,它不能在最佳化器階段計算,也不能在執行器初始階段計算,只能在執行器運行時計算。
使用EXPLAIN ANALYZE可以看出前三個季度的分區都是標記了(never executed),這就是執行器運行時剪枝。 它適用於易變的運算式值、子查詢、子串連,以及join條件運算式。可以看到SQL中的限定條件是分區鍵logdate,且條件運算式的值(select to_date('2023-10-1'))是易變的子串連,它可以在執行器運行階段計算出來。
EXPLAIN ANALYZE SELECT * FROM measurement WHERE logdate >= (select to_date('2023-10-1'));
QUERY PLAN
-----------------------------------------------------------------------------------------------------------------------
Append (cost=0.01..136.35 rows=2268 width=20) (actual time=0.067..0.068 rows=0 loops=1)
InitPlan 1 (returns $0)
-> Result (cost=0.00..0.01 rows=1 width=8) (actual time=0.051..0.053 rows=1 loops=1)
-> Seq Scan on measurement_y2023q1 (cost=0.00..31.25 rows=567 width=20) (never executed)
Filter: (logdate >= $0)
-> Seq Scan on measurement_y2023q2 (cost=0.00..31.25 rows=567 width=20) (never executed)
Filter: (logdate >= $0)
-> Seq Scan on measurement_y2023q3 (cost=0.00..31.25 rows=567 width=20) (never executed)
Filter: (logdate >= $0)
-> Seq Scan on measurement_y2023q4 (cost=0.00..31.25 rows=567 width=20) (actual time=0.004..0.004 rows=0 loops=1)
Filter: (logdate >= $0)樣本
SELECT
UPDATE、DELETE
相關參考
約束排除
參數
參數名稱 | 描述 |
constraint_exclusion | 用於控制約束排除,取值範圍如下:
|
當您啟用了約束排除,伺服器就會檢查定義給每個分區的約束來決定分區是否能滿足查詢。
當執行不包含WHERE子句的SELECT語句時,查詢計劃會推薦用於搜尋整個表的執行計畫。
當執行包含WHERE子句的SELECT語句時,查詢計劃會決定要儲存記錄的分區,並發送查詢片段給這個分區,然後從執行計畫中剪去不能包含記錄的分區。
如果您不使用分區表功能,則建議禁用約束排除,從而提高效能。
分區剪枝和約束排除的區別
分區剪枝和約束排除之間的區別在於:
分區剪枝瞭解分區表中分區之間的關係。 約束排除則不然。
例如,當查詢在列表分區表中搜尋特定值時,分區剪枝可能會導致只有特定分區可以儲存該值。 約束排除必須檢查為每個分區定義的約束。
分區剪枝發生在最佳化器的早期,以減少規劃者必須考慮的分區數量。 約束排除發生在最佳化器的後期。