PolarDB PostgreSQL版支持ePQ查看与分析执行计划。
背景信息
PostgreSQL提供了EXPLAIN
命令用于SQL语句的性能分析,该命令可以输出SQL对应的查询计划,以及在执行过程中的具体耗时、资源消耗等信息,可用于排查SQL的性能瓶颈。
但EXPLAIN
命令只适用于单机执行的SQL性能分析,PolarDB PostgreSQL版的ePQ扩展了EXPLAIN
的功能,使其可以打印ePQ的跨机并行执行计划,还可以统计ePQ执行计划在各个算子上的执行时间、数据扫描量、内存使用等信息,并以统一的视角提供给用户。
前提条件
支持的PolarDB PostgreSQL版的版本如下:
PostgreSQL 11(内核小版本1.1.22及以上)
PostgreSQL 14(内核小版本14.6.6.0及以上)
您可通过如下语句查看PolarDB PostgreSQL版的内核小版本的版本号:
PostgreSQL 11
show polar_version;
PostgreSQL 14
select version();
原理介绍
ePQ查询的发起进程(QC)与工作进程(Worker)之间采用libpq
的Y
协议进行通信:
QC将
EXPLAIN ANALYZE
命令下发给Worker。各个Worker进程统计本进程内的资源使用、执行耗时等信息。
各个Worker在完成计划分片的执行后,将统计信息发送给QC。
QC等待所有Worker进程完成各自执行后,进行统计计算并输出。
功能介绍
执行计划查看
ePQ的执行计划是分片的。每个计划分片(Slice)由计算节点上的虚拟执行单元(Segment)启动的一组进程(Gang)负责执行,完成SQL的一部分计算。ePQ在执行计划中引入了Motion算子,用于在执行不同计划分片的进程组之间进行数据传递。因此,Motion算子是计划分片的边界。
ePQ中总共引入了三种Motion算子:
PX Coordinator
:源端数据发送到同一个目标端(汇聚)。PX Broadcast
:源端数据发送到每一个目标端(广播)。PX Hash
:源端数据经过哈希计算后发送到某一个目标端(重分布)。
示例
以下为一个简单查询示例:
CREATE TABLE t (id INT); SET polar_enable_px TO ON; EXPLAIN (COSTS OFF) SELECT * FROM t LIMIT 1; QUERY PLAN ------------------------------------------------- Limit -> PX Coordinator 6:1 (slice1; segments: 6) -> Partial Seq Scan on t Optimizer: PolarDB PX Optimizer (4 rows)
以上执行计划以Motion算子为界,被分为了两个分片:一个是接收最终结果的分片
slice0
,一个是扫描数据的分片slice1
。对于
slice1
计划分片,ePQ将使用六个执行单元(segments: 6
)分别启动一个进程来执行,这六个进程各自负责扫描表的一部分数据(Partial Seq Scan
),通过Motion算子将六个进程的数据汇聚到一个目标端(PX Coordinator 6:1
),传递给Limit
算子。如果查询逐渐复杂,则执行计划中的计划分片和Motion算子会越来越多,示例如下:
CREATE TABLE t1 (a INT, b INT, c INT); SET polar_enable_px TO ON; EXPLAIN (COSTS OFF) SELECT SUM(b) FROM t1 GROUP BY a LIMIT 1; QUERY PLAN ------------------------------------------------------------ Limit -> PX Coordinator 6:1 (slice1; segments: 6) -> GroupAggregate Group Key: a -> Sort Sort Key: a -> PX Hash 6:6 (slice2; segments: 6) Hash Key: a -> Partial Seq Scan on t1 Optimizer: PolarDB PX Optimizer (10 rows)
以上执行计划中总共有三个计划分片。将会有六个进程(
segments: 6
)负责执行slice2
分片,分别扫描表的一部分数据,然后通过Motion算子(PX Hash 6:6
)将数据重分布到另外六个(segments: 6
)负责执行slice1
分片的进程上,各自完成排序(Sort
)和聚合(GroupAggregate
),最终通过Motion算子(PX Coordinator 6:1
)将数据汇聚到结果分片slice0
。
执行计划分析
EXPLAIN
中的ANALYZE
选项会使查询被真正执行,并收集执行过程中的各项统计信息,而不仅仅是打印执行计划。在ePQ的执行计划中,同一个算子会被一组进程执行。因此,ePQ的EXPLAIN ANALYZE
需要收集执行同一个算子的所有进程的统计信息。
算子级别的统计信息如下:
算子执行时间:取执行该算子的所有进程的最长执行时间。
算子扫描总行数:取执行该算子的所有进程的扫描行数累加和。
算子执行次数(循环):取执行该算子的所有进程的循环次数累加和。
算子的资源使用信息:取执行该算子的所有进程的资源使用量累加和。
此外,ePQ的EXPLAIN ANALYZE
还可以收集执行同一算子的各进程级别的统计信息,可被用于判断计划执行过程中是否存在倾斜。具体包含信息如下:
每个进程的内存使用情况。
每个进程的执行时间。
每个进程的处理行数。
示例
创建一张表并插入数据。
CREATE TABLE t2 (a INT, b INT, c VARCHAR(20)); INSERT INTO t2 SELECT i, i*2, to_char(i, 'FM00000') FROM generate_series(1, 100000) i;
设置如下参数并执行
EXPLAIN ANALYZE
。SET polar_enable_px TO ON; SET polar_px_enable_explain_all_stat TO ON; SET polar_px_explain_memory_verbosity TO detail; EXPLAIN (COSTS OFF, ANALYZE) SELECT * FROM t2; QUERY PLAN ------------------------------------------------------------------------------------------- PX Coordinator 6:1 (slice1; segments: 6) (actual time=0.816..54.225 rows=100000 loops=1) Executor Memory: 9kB Workers: 1 Max: 9kB (worker -1) -> Partial Seq Scan on t2 (actual time=0.052..24.732 rows=94720 loops=1) Executor Memory: 326kB Workers: 6 Max: 145kB (worker 1) allstat: worker:0, first_time:7.396(ms), total_time:25(ms), total_num:94720 worker:1, first_time:7.396(ms), total_time:2.819(ms), total_num:5280 worker:2, first_time:7.393(ms), total_time:0.074(ms), total_num:0 worker:3, first_time:7.400(ms), total_time:0.078(ms), total_num:0 worker:4, first_time:7.402(ms), total_time:0.086(ms), total_num:0 worker:5, first_time:7.399(ms), total_time:0.098(ms), total_num:0 Dynamic Pages Per Worker: [512,29] Planning Time: 9.768 ms Optimizer: PolarDB PX Optimizer (slice0) Executor memory: 38K bytes. (slice1) Executor memory: 68K bytes avg x 6 workers, 164K bytes max (seg1). Execution Time: 65.572 ms (17 rows)
在上述执行计划中:
每个算子下的
Executor Memory
部分打印了执行这个算子的所有进程所使用的总内存量、进程数量以及使用内存量最大的进程编号。allstat
部分打印了执行算子的每一个进程的准备时间(first_time
)、执行时间(total_time
)和处理元组数量(total_num
)。