PolarDB PostgreSQL版的ePQ支持分区表查询功能。
背景信息
随着数据量的不断增长,表的规模将会越来越大。为了方便管理和提高查询性能,用户一般会使用分区表,将大表拆分成多张子分区表,每张子分区表又可以进一步拆分成二级子分区表,从而形成了多级分区表。
PolarDB PostgreSQL版支持弹性跨机并行查询,能够利用集群中多个计算节点提升只读查询的性能。ePQ不仅能够对普通表进行高效的跨机并行查询,对分区表也实现了跨机并行查询。
ePQ对分区表支持的基础功能如下:
对分区策略为Range/List/Hash的分区表进行并行扫描。
对分区表进行索引扫描。
对分区表进行连接查询。
此外,ePQ还支持了部分与分区表相关的高级功能:
分区裁剪。
智能分区连接(Partition Wise Join)。
对多级分区表进行并行查询。
ePQ暂不支持对具有多列分区键的分区表进行并行查询。
前提条件
支持的PolarDB PostgreSQL版的版本如下:
PostgreSQL 11(内核小版本1.1.17及以上)
PostgreSQL 14(内核小版本14.8.11.0及以上)
您可通过如下语句查看PolarDB PostgreSQL版的内核小版本的版本号:
PostgreSQL 11
show polar_version;
PostgreSQL 14
select version();
使用指南
分区表并行查询
创建一张分区策略为Range的分区表,并创建三个子分区。
CREATE TABLE t1 (id INT) PARTITION BY RANGE(id); CREATE TABLE t1_p1 PARTITION OF t1 FOR VALUES FROM (0) TO (200); CREATE TABLE t1_p2 PARTITION OF t1 FOR VALUES FROM (200) TO (400); CREATE TABLE t1_p3 PARTITION OF t1 FOR VALUES FROM (400) TO (600);
开启ePQ和ePQ分区表扫描功能。
SET polar_enable_px TO ON; SET polar_px_enable_partition TO ON;
查看对分区表进行全表扫描的执行计划。
EXPLAIN (COSTS OFF) SELECT * FROM t1; QUERY PLAN ------------------------------------------- PX Coordinator 6:1 (slice1; segments: 6) -> Append -> Partial Seq Scan on t1_p1 -> Partial Seq Scan on t1_p2 -> Partial Seq Scan on t1_p3 Optimizer: PolarDB PX Optimizer (6 rows)
在上述执行计划中,ePQ将会启动一组进程并行扫描分区表的每一个子表。每一个扫描进程都会通过
Append
算子依次扫描每一个子表的一部分数据(Partial Seq Scan
),并通过Motion算子(PX Coordinator
)将所有进程的扫描结果汇聚到发起查询的进程并返回。
分区静态裁剪
当查询的过滤条件中包含分区键时,ePQ优化器可以根据过滤条件对需要扫描的分区表进行裁剪,避免扫描不需要的子分区,节省系统资源,提升查询性能。以上述t1
表为例,查看以下查询的执行计划。
EXPLAIN (COSTS OFF) SELECT * FROM t1 WHERE id < 100;
QUERY PLAN
-------------------------------------------
PX Coordinator 6:1 (slice1; segments: 6)
-> Append
-> Partial Seq Scan on t1_p1
Filter: (id < 100)
Optimizer: PolarDB PX Optimizer
(5 rows)
由于查询的过滤条件id < 100
包含分区键,因此ePQ优化器可以根据分区表的分区边界,在产生执行计划时去掉不符合过滤条件的子分区(t1_p2
、t1_p3
),只保留符合过滤条件的子分区(t1_p1
)。
智能分区连接
在进行分区表之间的连接操作时,如果分区策略和边界相同,并且连接条件为分区键时,ePQ优化器可以产生以子分区为单位进行连接的执行计划,避免两张分区表进行笛卡尔积式的连接,节省系统资源,提升查询性能。
以两张Range分区表的连接为例。
创建两张分区策略和边界都相同的分区表
t2
和t3
。CREATE TABLE t2 (id INT) PARTITION BY RANGE(id); CREATE TABLE t2_p1 PARTITION OF t2 FOR VALUES FROM (0) TO (200); CREATE TABLE t2_p2 PARTITION OF t2 FOR VALUES FROM (200) TO (400); CREATE TABLE t2_p3 PARTITION OF t2 FOR VALUES FROM (400) TO (600); CREATE TABLE t3 (id INT) PARTITION BY RANGE(id); CREATE TABLE t3_p1 PARTITION OF t3 FOR VALUES FROM (0) TO (200); CREATE TABLE t3_p2 PARTITION OF t3 FOR VALUES FROM (200) TO (400); CREATE TABLE t3_p3 PARTITION OF t3 FOR VALUES FROM (400) TO (600);
开启ePQ对分区表的支持。
SET polar_enable_px TO ON; SET polar_px_enable_partition TO ON;
关闭Partition Wise join时,两表在分区键上等值连接的执行计划如下。
SET polar_px_enable_partitionwise_join TO OFF; EXPLAIN (COSTS OFF) SELECT * FROM t2 JOIN t3 ON t2.id = t3.id; QUERY PLAN ----------------------------------------------------------- PX Coordinator 6:1 (slice1; segments: 6) -> Hash Join Hash Cond: (t2_p1.id = t3_p1.id) -> Append -> Partial Seq Scan on t2_p1 -> Partial Seq Scan on t2_p2 -> Partial Seq Scan on t2_p3 -> Hash -> PX Broadcast 6:6 (slice2; segments: 6) -> Append -> Partial Seq Scan on t3_p1 -> Partial Seq Scan on t3_p2 -> Partial Seq Scan on t3_p3 Optimizer: PolarDB PX Optimizer (14 rows)
从执行计划中可以看出,执行
slice1
计划分片的六个进程会分别通过Append
算子依次扫描分区表t2
每一个子分区的一部分数据,并通过Motion算子(PX Broadcast
)接收来自执行slice2
的六个进程广播的t3
全表数据,在本地完成哈希连接(Hash Join
)后,通过Motion算子(PX Coordinator
)汇聚结果并返回。本质上,分区表t2
的每一行数据都与t3
的每一行数据做了一次连接。开启Partition Wise join后,再次查看执行计划。
SET polar_px_enable_partitionwise_join TO ON; EXPLAIN (COSTS OFF) SELECT * FROM t2 JOIN t3 ON t2.id = t3.id; QUERY PLAN ------------------------------------------------ PX Coordinator 6:1 (slice1; segments: 6) -> Append -> Hash Join Hash Cond: (t2_p1.id = t3_p1.id) -> Partial Seq Scan on t2_p1 -> Hash -> Full Seq Scan on t3_p1 -> Hash Join Hash Cond: (t2_p2.id = t3_p2.id) -> Partial Seq Scan on t2_p2 -> Hash -> Full Seq Scan on t3_p2 -> Hash Join Hash Cond: (t2_p3.id = t3_p3.id) -> Partial Seq Scan on t2_p3 -> Hash -> Full Seq Scan on t3_p3 Optimizer: PolarDB PX Optimizer (18 rows)
在上述执行计划中,执行
slice1
计划分片的六个进程将通过Append
算子依次扫描分区表t2
每个子分区中的一部分数据,以及分区表t3
相对应子分区的全部数据,将两份数据进行哈希连接(Hash Join
),最终通过Motion算子(PX Coordinator
)汇聚结果并返回。在上述执行过程中,分区表
t2
的每一个子分区t2_p1
、t2_p2
、t2_p3
分别只与分区表t3
对应的t3_p1
、t3_p2
、t3_p3
做了连接,并没有与其它不相关的分区连接,提高了效率。
多级分区表并行查询
在多级分区表中,每级分区表的分区维度(分区键)可以不同:例如,一级分区表按照时间维度分区,二级分区表按照地域维度分区。当查询SQL的过滤条件中包含每一级分区表中的分区键时,ePQ优化器支持对多级分区表进行静态分区裁剪,从而过滤掉不需要被扫描的子分区。
如下图所示:当查询过滤条件WHERE date = '202201' AND region = 'beijing'
中包含一级分区键date
和二级分区键region
时,ePQ优化器能够裁剪掉所有不相关的分区,产生的执行计划中只包含符合条件的子分区。由此,执行器只对需要扫描的子分区进行扫描。
示例
创建一张多级分区表。
CREATE TABLE r1 (a INT, b TIMESTAMP) PARTITION BY RANGE (b); CREATE TABLE r1_p1 PARTITION OF r1 FOR VALUES FROM ('2000-01-01') TO ('2010-01-01') PARTITION BY RANGE (a); CREATE TABLE r1_p1_p1 PARTITION OF r1_p1 FOR VALUES FROM (1) TO (1000000); CREATE TABLE r1_p1_p2 PARTITION OF r1_p1 FOR VALUES FROM (1000000) TO (2000000); CREATE TABLE r1_p2 PARTITION OF r1 FOR VALUES FROM ('2010-01-01') TO ('2020-01-01') PARTITION BY RANGE (a); CREATE TABLE r1_p2_p1 PARTITION OF r1_p2 FOR VALUES FROM (1) TO (1000000); CREATE TABLE r1_p2_p2 PARTITION OF r1_p2 FOR VALUES FROM (1000000) TO (2000000);
开启ePQ对分区表的支持。
SET polar_enable_px TO ON; SET polar_px_enable_partition TO ON;
执行一条以两级分区键作为过滤条件的SQL,并关闭ePQ的多级分区扫描功能,得到PostgreSQL内置优化器经过多级分区静态裁剪后的执行计划。
SET polar_px_optimizer_multilevel_partitioning TO OFF; EXPLAIN (COSTS OFF) SELECT * FROM r1 WHERE a < 1000000 AND b < '2009-01-01 00:00:00'; QUERY PLAN ---------------------------------------------------------------------------------------- Seq Scan on r1_p1_p1 r1 Filter: ((a < 1000000) AND (b < '2009-01-01 00:00:00'::timestamp without time zone)) (2 rows)
开启ePQ的多级分区扫描功能,再次查看执行计划。
SET polar_px_optimizer_multilevel_partitioning TO ON; EXPLAIN (COSTS OFF) SELECT * FROM r1 WHERE a < 1000000 AND b < '2009-01-01 00:00:00'; QUERY PLAN ---------------------------------------------------------------------------------------------------- PX Coordinator 6:1 (slice1; segments: 6) -> Append -> Partial Seq Scan on r1_p1_p1 Filter: ((a < 1000000) AND (b < '2009-01-01 00:00:00'::timestamp without time zone)) Optimizer: PolarDB PX Optimizer (5 rows)
在上述计划中,ePQ优化器进行了对多级分区表的静态裁剪。执行
slice1
计划分片的六个进程只需对符合过滤条件的子分区r1_p1_p1
进行并行扫描(Partial Seq Scan
),并将扫描到的数据通过Motion算子(PX Coordinator
)汇聚并返回。