PolarDB PostgreSQL版实现了弹性跨机并行查询(ePQ)特性,能够帮助您解决原先的PolarDB PostgreSQL版在处理复杂的AP查询时会遇到的问题。
前提条件
支持的PolarDB PostgreSQL版的版本如下:PostgreSQL 11(内核小版本1.1.28及以上)
show polar_version;
背景信息
- 单条SQL在原生PostgreSQL执行引擎下只能在单个节点上执行,无论是单机串行还是单机并行,都无法利用其他节点的CPU、内存等计算资源,只能纵向Scale Up,不能横向Scale Out。
- PolarDB PostgreSQL版底层是存储池,理论上I/O吞吐是无限大的。而单条SQL在原生PostgreSQL执行引擎下只能在单个节点上执行,受限于单节点CPU和内存的瓶颈,无法充分发挥存储侧大I/O带宽的优势。

- TP和AP在存储和计算上完全分离。
- 优势:两种业务负载互不影响。
- 劣势:
- 时效性:TP的数据需要导入到AP系统中,存在一定的延迟。
- 成本/运维难度:增加了一套冗余的AP系统。
- TP和AP在存储和计算上完全共享。
- 优势:成本最小化、资源利用最大化。
- 劣势:
- 计算共享会导致AP查询和TP查询同时运行时会存在相互影响。
- 扩展计算节点存储时,数据需要重分布,无法快速弹性Scale Out。
- TP和AP在存储上共享,在计算上分离。说明 本身的存储计算分离架构天然支持此方案。
HTAP特性原理
- 架构特性。基于PolarDB PostgreSQL版的存储计算分离架构,推出了分布式ePQ执行引擎,提供了跨机并行执行、弹性计算、弹性扩展的保证,使得PolarDB PostgreSQL版初步具备了HTAP的能力。其优势如下:
- 一体化存储:毫秒级数据新鲜度。
TP/AP共享一套存储数据,减少存储成本,提高查询时效。
- TP/AP物理隔离:杜绝CPU/内存的相互影响。
- 单机执行引擎:在RW/RO节点,处理高并发的TP查询。
- 分布式ePQ执行引擎:在RO节点,处理高复杂度的AP查询。
- Serverless弹性扩展:任何一个RO节点均可发起ePQ查询。
- Scale Out:弹性调整ePQ的执行节点范围。
- Scale Up:弹性调整ePQ的单机并行度。
- 消除数据倾斜、计算倾斜,充分考虑PolarDB PostgreSQL版的Buffer Pool亲和性。
- 一体化存储:毫秒级数据新鲜度。
- 分布式ePQ执行引擎。PolarDB PostgreSQL版ePQ的核心是分布式ePQ执行引擎,是典型的火山模型引擎。A、B两张表先做join再做聚合输出,这也是PostgreSQL单机执行引擎的执行流程。执行流程如下所示:
在传统的mpp执行引擎中,数据被打散到不同的节点上,不同节点上的数据可能具有不同的分布属性,例如哈希分布、随机分布、复制分布等。传统的mpp执行引擎会针对不同表的数据分布特点,在执行计划中插入算子来保证上层算子对数据的分布属性无感知。
不同的是,PolarDB PostgreSQL版是共享存储架构,存储上的数据可以被所有计算节点全量访问。如果使用传统的mpp执行引擎,每个计算节点Worker都会扫描全量数据,从而得到重复的数据。同时,也没有起到扫描时分治加速的效果,并不能称得上是真正意义上的mpp引擎。
因此,在分布式ePQ执行引擎中,我们借鉴了火山模型论文中的思想,对所有扫描算子进行并发处理,引入了PxScan算子来屏蔽共享存储。PxScan算子将shared-storage的数据映射为shared-nothing的数据,通过Worker之间的协调,将目标表划分为多个虚拟分区数据块,每个Worker扫描各自的虚拟分区数据块,从而实现了跨机分布式并行扫描。
PxScan算子扫描出来的数据会通过Shuffle算子来重分布。重分布后的数据在每个Worker上如同单机执行一样,按照火山模型来执行。
- Serverless弹性扩展。传统mpp只能在指定节点发起mpp查询,因此每个节点上都只能有单个Worker扫描一张表。为了支持云原生下Serverless弹性扩展的需求,我们引入了分布式事务一致性保障。如图所示:任意选择一个节点作为Coordinator节点,它的ReadLSN会作为约定的LSN,从所有ePQ节点的快照版本号中选择最小的版本号作为全局约定的快照版本号。通过LSN的回放等待和Global Snapshot同步机制,确保在任何一个节点发起ePQ查询时,数据和快照均能达到一致可用的状态。如图所示:
为了实现Serverless的弹性扩展,PolarDB PostgreSQL版从共享存储的特点出发,将Coordinator节点全链路上各个模块需要的外部依赖全部放至共享存储上。各个Worker节点运行时需要的参数也会通过控制链路从Coordinator节点同步过来,从而使Coordinator节点和Worker节点全链路无状态化(Stateless)。
基于以上两点设计,PolarDB PostgreSQL版的弹性扩展具备了以下几大优势:- 任何节点都可以成为Coordinator节点,解决了传统mpp数据库Coordinator节点的单点问题。
- PolarDB PostgreSQL版可以横向Scale Out(计算节点数量),也可以纵向Scale Up(单节点并行度),且弹性扩展即时生效,不需要重新分布数据。
- 允许业务有更多的弹性调度策略,不同的业务域可以运行在不同的节点集合上。业务域1的SQL可以选择RO1和RO2节点来执行AP查询,业务域2的SQL可以选择使用RO3和RO4节点来执行AP查询,两个业务域使用的计算节点可以实现弹性调度。
- 消除倾斜。倾斜是传统mpp固有的问题,其根本原因主要是数据分布倾斜和数据计算倾斜。倾斜会导致传统mpp在执行时出现木桶效应,执行完成时间受制于执行最慢的子任务。
- 数据分布倾斜通常由数据打散不均衡导致,在PostgreSQL中还会由于大对象Toast表存储引入一些不可避免的数据分布不均衡问题。
- 计算倾斜通常由于不同节点上并发的事务、Buffer Pool、网络、I/O抖动导致。
PolarDB PostgreSQL版设计并实现了自适应扫描机制。如下图所示,采用Coordinator节点来协调Worker节点的工作模式。在扫描数据时,Coordinator节点会在内存中创建一个任务管理器,根据扫描任务对Worker节点进行调度。Coordinator节点内部分为两个线程。
- Data线程主要负责服务数据链路、收集汇总元组。
- Control线程负责服务控制链路、控制每一个扫描算子的扫描进度。
扫描进度较快的Worker能够扫描多个数据块。如上图中RO1与RO3的Worker各自扫描了4个数据块, RO2由于计算倾斜可以扫描更多数据块,因此它最终扫描了6个数据块。
PolarDB PostgreSQL版ePQ的自适应扫描机制还充分考虑了PostgreSQL的Buffer Pool亲和性,保证每个Worker尽可能扫描固定的数据块,从而最大化命中Buffer Pool的概率,降低I/O开销。
TPC-H性能对比
- 单机并行与分布式ePQ对比。使用256 GB内存的16个PolarDB PostgreSQL版集群作为RO节点,搭建1 TB的TPC-H环境进行对比测试。相较于单机并行,分布式ePQ并行充分利用了所有RO节点的计算资源和底层共享存储的I/O带宽,从根本上解决了前文提及的HTAP诸多挑战。在TPC-H的22条SQL中,有3条SQL加速了60多倍,19条SQL加速了10多倍,平均加速23倍。如图所示:测试弹性扩展计算资源带来的性能变化。通过增加CPU的总核心数,从16核增加到128核,TPC-H的总运行时间线性提升,每条SQL的执行速度也呈线性提升,这也验证了PolarDB PostgreSQL版ePQ Serverless弹性扩展的特点。如图所示:
在测试中发现,当CPU的总核数增加到256核时,性能提升不再明显。原因是此时PolarDB PostgreSQL版共享存储的I/O带宽已经达到100%,成为了瓶颈。
- PolarDB与传统mpp数据库对比。
同样使用256 GB内存的16个节点,将PolarDB PostgreSQL版的分布式ePQ执行引擎与传统数据库的mpp执行引擎进行对比。
在1 TB的TPC-H数据上,当保持与传统mpp数据库相同单机并行度的情况下(多机单进程),PolarDB PostgreSQL版的性能是传统mpp数据库的90%。其中最本质的原因是传统mpp数据库的数据默认是哈希分布的,当两张表的join key是各自的分布键时,可以不用shuffle直接进行本地的Wise Join。而PolarDB PostgreSQL版的底层是共享存储池,PxScan算子并行扫描出来的数据等价于随机分布,必须进行shuffle重分布以后才能像传统mpp数据库一样进行后续的处理。因此,TPC-H涉及到表连接时,PolarDB PostgreSQL版相比传统mpp数据库多了一次网络shuffle的开销。如图所示:PolarDB PostgreSQL版分布式ePQ执行引擎能够进行弹性扩展,数据无需重分布。因此,在有限的16台机器上执行ePQ时,PolarDB PostgreSQL版还可以继续扩展单机并行度,充分利用每台机器的资源。当PolarDB PostgreSQL版的单机并行度为8时,它的性能是传统mpp数据库的5-6倍;当PolarDB PostgreSQL版的单机并行度呈线性增加时,PolarDB PostgreSQL版的总体性能也呈线性增加。只需要修改配置参数,就可以即时生效。
功能特性
- Parallel Query并行查询。经过持续迭代的研发,目前PolarDB PostgreSQL版ePQ在Parallel Query上支持的功能特性主要有五大部分:
- 基础算子全支持:扫描/连接/聚合/子查询等算子。
- 共享存储算子优化:包括Shuffle算子共享、SharedSeqScan共享、SharedIndexScan算子等。其中SharedSeqScan共享、SharedIndexScan共享是指,在大表join小表时,小表采用类似于复制表的机制来减少广播开销,进而提升性能。
- 分区表支持:不仅包括对Hash/Range/List三种分区方式的完整支持,还包括对多级分区静态裁剪、分区动态裁剪的支持。除此之外,PolarDB PostgreSQL版分布式ePQ执行引擎还支持分区表的Partition Wise Join。
- 并行度弹性控制:包括全局级别、表级别、会话级别、查询级别的并行度控制。
- Serverless弹性扩展:不仅包括任意节点发起ePQ、ePQ节点范围内的任意组合,还包括集群拓扑信息的自动维护以及支持共享存储模式、主备库模式和三节点模式。
- Parallel DML。基于PolarDB PostgreSQL版读写分离架构和ePQ Serverless弹性扩展的设计, PolarDB PostgreSQL版Parallel DML支持一写多读、多写多读两种特性。
- 一写多读:在RO节点上有多个读Worker,在RW节点上只有一个写Worker。
- 多写多读:在RO节点上有多个读Worker,在RW节点上也有多个写Worker。多写多读场景下,读写的并发度完全解耦。
不同的特性适用不同的场景,用户可以根据自己的业务特点来选择不同的Parallel DML功能特性。
- 索引构建加速。PolarDB PostgreSQL版分布式ePQ执行引擎,不仅可以用于只读查询和DML,还可以用于索引构建加速。OLTP业务中有大量的索引,而B-Tree索引创建的过程大约有80%的时间消耗在排序和构建索引页上,20%消耗在写入索引页上。如下图所示:
PolarDB PostgreSQL版利用RO节点对数据进行分布式ePQ加速排序,采用流水化的技术来构建索引页,同时使用批量写入技术来提升索引页的写入速度。
说明 在目前索引构建加速这一特性中,PolarDB PostgreSQL版已经支持了B-Tree索引的普通创建以及B-Tree索引的在线创建(Concurrently)两种功能。
使用指南
PolarDB PostgreSQL版ePQ适用于日常业务中的轻分析类业务,例如:对账业务,报表业务。
- 使用ePQ进行分析型查询。PolarDB PostgreSQL版默认不开启ePQ功能。若您需要使用此功能,请使用如下参数:
参数 说明 polar_enable_px 指定是否开启ePQ功能。 - on:开启ePQ功能。
- off:(默认值)不开启ePQ功能。
polar_px_max_workers_number 设置单个节点上的最大ePQ Worker进程数,默认为30,取值范围为0~2147483647。该参数限制了单个节点上的最大并行度。 说明 节点上所有会话的ePQ workers进程数不能超过该参数大小。polar_px_dop_per_node 设置当前会话并行查询的并行度,默认为1,推荐值为当前CPU 总核数,取值范围为1~128。若设置该参数为N,则一个会话在每个节点上将会启用N个ePQ Worker进程,用于处理当前的ePQ逻辑。 polar_px_nodes 指定参与ePQ的只读节点。默认为空,表示所有只读节点都参与。可配置为指定节点参与ePQ,以逗号分隔。 px_worker 指定ePQ是否对特定表生效。默认不生效。ePQ功能比较消耗集群计算节点的资源,因此只有对设置了px_workers的表才使用该功能。例如: - ALTER TABLE t1 SET(px_workers=1):表示t1表允许ePQ。
- ALTER TABLE t1 SET(px_workers=-1):表示t1表禁止ePQ。
- ALTER TABLE t1 SET(px_workers=0):表示t1表忽略ePQ(默认状态)。
以简单的单表查询操作,来描述ePQ的功能是否有效。- 创建test表并插入基础数据。
CREATE TABLE test(id int); INSERT INTO test SELECT generate_series(1,1000000);
- 查询执行计划。
执行结果如下:EXPLAIN SELECT * FROM test;
QUERY PLAN -------------------------------------------------------- Seq Scan on test (cost=0.00..35.50 rows=2550 width=4) (1 row)
说明 默认情况下ePQ功能不开启,单表查询执行计划为PG原生的Seq Scan。 - 开启并使用ePQ功能。
结果如下:ALTER TABLE test SET (px_workers=1); SET polar_enable_px = on; EXPLAIN SELECT * FROM test;
QUERY PLAN ------------------------------------------------------------------------------- PX Coordinator 2:1 (slice1; segments: 2) (cost=0.00..431.00 rows=1 width=4) -> Seq Scan on test (scan partial) (cost=0.00..431.00 rows=1 width=4) Optimizer: PolarDB PX Optimizer (3 rows)
- 配置参与ePQ的计算节点范围。
- 查询当前所有只读节点的名称。
结果如下:CREATE EXTENSION polar_monitor; SELECT name,host,port FROM polar_cluster_info WHERE px_node='t';
name | host | port -------+-----------+------ node1 | 127.0.0.1 | 5433 node2 | 127.0.0.1 | 5434 (2 rows)
说明 当前集群有2个只读节点,名称分别为:node1,node2。 - 指定node1只读节点参与ePQ。
SET polar_px_nodes = 'node1';
- 查询参与并行查询的节点。
结果如下:SHOW polar_px_nodes;
polar_px_nodes ---------------- node1 (1 row)
说明 参与并行查询的节点是node1。 - 查询test表中所有数据。
结果如下:EXPLAIN SELECT * FROM test;
QUERY PLAN ------------------------------------------------------------------------------- PX Coordinator 1:1 (slice1; segments: 1) (cost=0.00..431.00 rows=1 width=4) -> Partial Seq Scan on test (cost=0.00..431.00 rows=1 width=4) Optimizer: PolarDB PX Optimizer (3 rows) QUERY PLAN
- 查询当前所有只读节点的名称。
- 使用ePQ进行分区表查询。
- 开启ePQ功能。
SET polar_enable_px = ON;
- 开启分区表ePQ功能。
SET polar_px_enable_partition = true;
说明 分区表ePQ功能默认关闭。 - 开启多级分区表ePQ功能。
SET polar_px_optimizer_multilevel_partitioning = true;
当前ePQ对分区表支持的功能如下所示:- 支持Range分区的并行查询。
- 支持List分区的并行查询。
- 支持单列Hash分区的并行查询。
- 支持分区裁剪。
- 支持带有索引的分区表并行查询。
- 支持分区表连接查询。
- 支持多级分区的并行查询。
- 开启ePQ功能。
- 使用ePQ加速索引创建。
- 开启使用ePQ加速创建索引功能。
SET polar_px_enable_btbuild = on;
- 创建索引。
CREATE INDEX t ON test(id) WITH(px_build = ON);
- 查询表结构。
结果如下:\d test
Table "public.test" Column | Type | Collation | Nullable | Default --------+---------+-----------+----------+--------- id | integer | | | id2 | integer | | | Indexes: "t" btree (id) WITH (px_build=finish)
说明 当前仅支持对B-Tree索引的构建,且暂不支持INCLUDE等索引构建语法,暂不支持表达式等索引列类型。如果需要使用ePQ功能加速创建索引,请使用如下参数:参数 说明 polar_px_dop_per_node 指定通过ePQ加速构建索引的并行度。默认为1,取值范围为1~128。 polar_px_enable_replay_wait 当使用ePQ加速索引构建时,当前会话内无需手动开启该参数,该参数将自动生效。以保证最近更新的数据表项可以被创建到索引中,保证索引表的完整性。索引创建完成后,该参数将会被重置为数据库默认值。 polar_px_enable_btbuild 是否开启使用ePQ加速创建索引。取值为OFF时不开启(默认),取值为ON时开启。 polar_bt_write_page_buffer_size 指定索引构建过程中的写I/O策略。该参数默认值为0(不开启),单位为块,最大值可设置为8192。推荐设置为4096。 - 参数值为0:该参数设置为不开启,在索引创建的过程中,对于索引页写满后的写盘方式是block-by-block的单个块写盘。
- 参数值不为0:该参数设置为开启,内核中将缓存一个polar_bt_write_page_buffer_size大小的buffer,对于需要写盘的索引页,会通过该buffer进行I/O合并再统一写盘,避免了频繁调度I/O带来的性能开销。该参数会额外提升20%的索引创建性能。
- 开启使用ePQ加速创建索引功能。