PotgreSQL 11 新特性之智慧分割槽連線/聚合
文章目錄
PostgreSQL 11 提供了兩個分割槽級別的新功能:
- 智慧分割槽連線(partition-wise join),對於兩個分割槽方式相同的分割槽表,如果使用分割槽鍵進行等值連線,允許使用匹配的分割槽直接進行連線操作。
- 智慧分割槽聚合(partition-wise aggregation),對於分割槽表的聚合操作,如果 GROUP BY 中包含了分割槽鍵,允許針對各個分割槽進行並行的聚合操作,然後再合併結果。
智慧分割槽連線
PostgreSQL 11 增加了一個新的選項:enable_partitionwise_join,用於控制查詢計劃器是否使用分割槽級別的連線。預設值為 off。
首先,建立兩個分割槽表,它們使用相同的分割槽方式:
CREATE TABLE prt1 (a int, b int, c varchar) PARTITION BY RANGE(a);
CREATE TABLE prt1_p1 PARTITION OF prt1 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt1_p3 PARTITION OF prt1 FOR VALUES FROM (500) TO (600);
CREATE TABLE prt1_p2 PARTITION OF prt1 FOR VALUES FROM (250) TO (500);
INSERT INTO prt1 SELECT i, i % 25, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 2 = 0;
CREATE INDEX iprt1_p1_a on prt1_p1(a);
CREATE INDEX iprt1_p2_a on prt1_p2(a);
CREATE INDEX iprt1_p3_a on prt1_p3(a);
ANALYZE prt1;
CREATE TABLE prt2 (a int, b int, c varchar) PARTITION BY RANGE(b);
CREATE TABLE prt2_p1 PARTITION OF prt2 FOR VALUES FROM (0) TO (250);
CREATE TABLE prt2_p2 PARTITION OF prt2 FOR VALUES FROM (250) TO (500);
CREATE TABLE prt2_p3 PARTITION OF prt2 FOR VALUES FROM (500) TO (600);
INSERT INTO prt2 SELECT i % 25, i, to_char(i, 'FM0000') FROM generate_series(0, 599) i WHERE i % 3 = 0;
CREATE INDEX iprt2_p1_b on prt2_p1(b);
CREATE INDEX iprt2_p2_b on prt2_p2(b);
CREATE INDEX iprt2_p3_b on prt2_p3(b);
ANALYZE prt2;
檢視預設情況下,兩個表的連線操作:
show enable_partitionwise_join;
enable_partitionwise_join
---------------------------
off
(1 row)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
--------------------------------------------------
Sort
Sort Key: t1.a
-> Hash Join
Hash Cond: (t2.b = t1.a)
-> Append
-> Seq Scan on prt2_p1 t2
-> Seq Scan on prt2_p2 t2_1
-> Seq Scan on prt2_p3 t2_2
-> Hash
-> Append
-> Seq Scan on prt1_p1 t1
Filter: (b = 0)
-> Seq Scan on prt1_p2 t1_1
Filter: (b = 0)
-> Seq Scan on prt1_p3 t1_2
Filter: (b = 0)
(16 rows)
預設情況下,需要先分別掃描兩個表的所有分割槽,將分割槽結果分別整合在一起(Append),然後執行兩個表的雜湊連線(Hash Join),最後進行排序(Sort)。
接下來,啟用智慧分割槽連線功能,即將引數 enable_partitionwise_join 設定為 on。再次檢視相同的連線操作:
set enable_partitionwise_join to true;
SET
rgsdb=# show enable_partitionwise_join;
enable_partitionwise_join
---------------------------
on
(1 row)
EXPLAIN (COSTS OFF)
SELECT t1.a, t1.c, t2.b, t2.c FROM prt1 t1, prt2 t2 WHERE t1.a = t2.b AND t1.b = 0 ORDER BY t1.a, t2.b;
QUERY PLAN
--------------------------------------------------
Sort
Sort Key: t1.a
-> Append
-> Hash Join
Hash Cond: (t2.b = t1.a)
-> Seq Scan on prt2_p1 t2
-> Hash
-> Seq Scan on prt1_p1 t1
Filter: (b = 0)
-> Hash Join
Hash Cond: (t2_1.b = t1_1.a)
-> Seq Scan on prt2_p2 t2_1
-> Hash
-> Seq Scan on prt1_p2 t1_1
Filter: (b = 0)
-> Hash Join
Hash Cond: (t2_2.b = t1_2.a)
-> Seq Scan on prt2_p3 t2_2
-> Hash
-> Seq Scan on prt1_p3 t1_2
Filter: (b = 0)
(21 rows)
可以看到,啟用該功能之後,先針對兩個表中的相應分割槽執行雜湊連線(Hash Join),然後將結果整合在一起(Append),最後進行排序(Sort)。其中,分割槽級別的雜湊連線可以並行執行,而且分割槽中的資料量比整個表少,效能會更好。
目前,智慧分割槽連線要求連線條件中必須包含所有的分割槽欄位,它們的資料型別必須相同,並且分割槽方式一致。由於智慧分割槽連線的計劃階段需要佔用更多的 CPU 時間和記憶體,預設沒有啟用該功能。
智慧分割槽聚合
智慧分割槽聚合(分組)使用選項 enable_partitionwise_aggregate 進行控制。預設值為 off。
首先,建立一個分割槽表:
CREATE TABLE pagg_tab (a int, b int, c text, d int) PARTITION BY LIST(c);
CREATE TABLE pagg_tab_p1 PARTITION OF pagg_tab FOR VALUES IN ('0000', '0001', '0002', '0003');
CREATE TABLE pagg_tab_p2 PARTITION OF pagg_tab FOR VALUES IN ('0004', '0005', '0006', '0007');
CREATE TABLE pagg_tab_p3 PARTITION OF pagg_tab FOR VALUES IN ('0008', '0009', '0010', '0011');
INSERT INTO pagg_tab SELECT i % 20, i % 30, to_char(i % 12, 'FM0000'), i % 30 FROM generate_series(0, 2999) i;
ANALYZE pagg_tab;
預設情況下的聚合操作:
show enable_partitionwise_aggregate;
enable_partitionwise_aggregate
--------------------------------
off
(1 row)
EXPLAIN (COSTS OFF)
SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3;
QUERY PLAN
-----------------------------------------------------------------------
Sort
Sort Key: pagg_tab_p1.c, (sum(pagg_tab_p1.a)), (avg(pagg_tab_p1.b))
-> HashAggregate
Group Key: pagg_tab_p1.c
Filter: (avg(pagg_tab_p1.d) < '15'::numeric)
-> Append
-> Seq Scan on pagg_tab_p1
-> Seq Scan on pagg_tab_p2
-> Seq Scan on pagg_tab_p3
(9 rows)
預設情況下,需要先分別掃描表的所有分割槽,將分割槽結果整合在一起(Append),然後執行雜湊聚合(HashAggregate),最後進行排序(Sort)。
啟用智慧分割槽聚合功能,檢視相同的聚合操作:
SET enable_partitionwise_aggregate TO true;
SET
EXPLAIN (COSTS OFF)
SELECT c, sum(a), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY c HAVING avg(d) < 15 ORDER BY 1, 2, 3;
QUERY PLAN
-----------------------------------------------------------------------
Sort
Sort Key: pagg_tab_p1.c, (sum(pagg_tab_p1.a)), (avg(pagg_tab_p1.b))
-> Append
-> HashAggregate
Group Key: pagg_tab_p1.c
Filter: (avg(pagg_tab_p1.d) < '15'::numeric)
-> Seq Scan on pagg_tab_p1
-> HashAggregate
Group Key: pagg_tab_p2.c
Filter: (avg(pagg_tab_p2.d) < '15'::numeric)
-> Seq Scan on pagg_tab_p2
-> HashAggregate
Group Key: pagg_tab_p3.c
Filter: (avg(pagg_tab_p3.d) < '15'::numeric)
-> Seq Scan on pagg_tab_p3
(15 rows)
可以看到,啟用該功能之後,先針對表中的所有分割槽執行雜湊聚合(HashAggregate),然後將結果整合在一起(Append),最後進行排序(Sort)。其中,分割槽級別的聚合可以並行執行,效能會更好。
如果 GROUP BY 子句中沒有包含分割槽欄位,只會基於分割槽執行部分聚合操作,然後再對結果進行一次最終的聚合。
以下查詢使用欄位 a 進行分組聚合:
EXPLAIN (COSTS OFF)
SELECT a, sum(b), avg(b), count(*), min(a), max(b) FROM pagg_tab GROUP BY a HAVING avg(d) < 15 ORDER BY 1, 2, 3;
QUERY PLAN
-----------------------------------------------------------------------
Sort
Sort Key: pagg_tab_p1.a, (sum(pagg_tab_p1.b)), (avg(pagg_tab_p1.b))
-> Finalize HashAggregate
Group Key: pagg_tab_p1.a
Filter: (avg(pagg_tab_p1.d) < '15'::numeric)
-> Append
-> Partial HashAggregate
Group Key: pagg_tab_p1.a
-> Seq Scan on pagg_tab_p1
-> Partial HashAggregate
Group Key: pagg_tab_p2.a
-> Seq Scan on pagg_tab_p2
-> Partial HashAggregate
Group Key: pagg_tab_p3.a
-> Seq Scan on pagg_tab_p3
(15 rows)
由於欄位 a 不是分割槽鍵,所以先執行分割槽級別的部分雜湊聚合(Partial HashAggregate),聚合的結果中可能存在相同的分組(不同分割槽中的欄位 a 存在相同的值),需要執行最終的雜湊聚合(Finalize HashAggregate)操作。
由於智慧分割槽聚合(分組)的計劃階段需要佔用更多的 CPU 時間和記憶體,預設沒有啟用該功能。
可以同時啟用智慧分割槽連線與智慧分割槽聚合功能。首先,建立兩個分割槽表:
CREATE TABLE pagg_tab1(x int, y int) PARTITION BY RANGE(x);
CREATE TABLE pagg_tab1_p1 PARTITION OF pagg_tab1 FOR VALUES FROM (0) TO (10);
CREATE TABLE pagg_tab1_p2 PARTITION OF pagg_tab1 FOR VALUES FROM (10) TO (20);
CREATE TABLE pagg_tab1_p3 PARTITION OF pagg_tab1 FOR VALUES FROM (20) TO (30);
CREATE TABLE pagg_tab2(x int, y int) PARTITION BY RANGE(y);
CREATE TABLE pagg_tab2_p1 PARTITION OF pagg_tab2 FOR VALUES FROM (0) TO (10);
CREATE TABLE pagg_tab2_p2 PARTITION OF pagg_tab2 FOR VALUES FROM (10) TO (20);
CREATE TABLE pagg_tab2_p3 PARTITION OF pagg_tab2 FOR VALUES FROM (20) TO (30);
INSERT INTO pagg_tab1 SELECT i % 30, i % 20 FROM generate_series(0, 299, 2) i;
INSERT INTO pagg_tab2 SELECT i % 20, i % 30 FROM generate_series(0, 299, 3) i;
ANALYZE pagg_tab1;
ANALYZE pagg_tab2;
使用分割槽欄位作為連線條件,同時使用分割槽欄位進行分組聚合:
SET enable_partitionwise_join TO true;
SET enable_partitionwise_aggregate TO true;
EXPLAIN (COSTS OFF)
rgsdb-# SELECT t1.x, sum(t1.y), count(*) FROM pagg_tab1 t1, pagg_tab2 t2 WHERE t1.x = t2.y GROUP BY t1.x ORDER BY 1, 2, 3;
QUERY PLAN
-------------------------------------------------------------
Sort
Sort Key: t1.x, (sum(t1.y)), (count(*))
-> Append
-> HashAggregate
Group Key: t1.x
-> Hash Join
Hash Cond: (t1.x = t2.y)
-> Seq Scan on pagg_tab1_p1 t1
-> Hash
-> Seq Scan on pagg_tab2_p1 t2
-> HashAggregate
Group Key: t1_1.x
-> Hash Join
Hash Cond: (t1_1.x = t2_1.y)
-> Seq Scan on pagg_tab1_p2 t1_1
-> Hash
-> Seq Scan on pagg_tab2_p2 t2_1
-> HashAggregate
Group Key: t1_2.x
-> Hash Join
Hash Cond: (t2_2.y = t1_2.x)
-> Seq Scan on pagg_tab2_p3 t2_2
-> Hash
-> Seq Scan on pagg_tab1_p3 t1_2
(24 rows)
執行計劃先針對兩個表中的相應分割槽執行雜湊連線(Hash Join),然後基於連線結果執行分割槽級別的雜湊聚合(HashAggregate),將結果整合在一起(Append),最後進行排序(Sort)。分割槽級別的雜湊連線以及分割槽級別的聚合可以並行執行。
另外,PostgreSQL 11 還支援 postgres_fdw 外部分割槽的聚合操作下推,即將聚合操作下推至外部表執行。同時還支援 postgres_fdw 外部分割槽上的 INSERT、UPDATE、COPY 操作。
參考:
Basic partition-wise join functionality.
Implement partition-wise grouping/aggregation.
人生本來短暫,你又何必匆匆!點個贊再走吧!