PostgreSQL 技术内幕 (十二)CloudberryDB 并行化查询之路
随着数据驱动的应用日益增多,数据查询和分析的量级和时效性要求也在不断提升,对数据库的查询性能提出了更高的要求。为了满足这一需求,数据库引擎不断经历创新,其中并行执行引擎是性能提升的重要手段之一,逐渐成为数据库系统的标配特性。
Cloudberry Database(简称为“CBDB”或“CloudberryDB”)是面向分析和 AI 场景打造的下一代统一型开源数据库,搭载了 PostgreSQL 14.4 内核,采用 Apache License 2.0 许可协议。CBDB 在 Postgres 的基础之上,对已有的并行执行计划进行了大量的调整和优化,实现了显著的性能提升。
在这次的直播中,HashData 数据库内核研发专家介绍了 Postgres 的并行化原理,CBDB 在并行化上的优化与改进、功能特性及实践演示。以下内容根据直播文字整理。
并行化查询介绍
PostgreSQL 在很多场景下会启用并行执行计划,创建多个并行工作子进程,提升查询效率。
PostgreSQL 的并行化包含三个重要组件:进程本身(leader 进程)、gather、workers。没有开启并行化的时候,进程自身处理所有的数据;一旦计划器决定某个查询或查询中某个部分可以使用并行的时候,就会在查询的并行化部分添加一个 gather 节点,将 gather 节点作为子查询树的根节点。
HashData 研发团队在对 CloudberryDB 实现并行化查询时,主要对查询执行算子、Join 的实现以及存储引擎并行化扫描等进行了调整和优化。
图 1:非并行化查询与并行并查询对比示例
如图 1 所示,以 3 个节点的集群为例,在不开启并行化进行 Not in 操作时,需要耗时 50 多秒;而在 CBDB 中开启并行化查询后,用时仅需 119 毫秒,效率大约提升 600 倍。
在开启并行化查询时,有以下几处变化:
Gather Motion 从 3:1 提升至 12:1,意味着集群的每个节点上有 4 个进程在同时并行工作;
Hash 节点变为 Parallel Hash;
新算子 Broadcast Workers Motion 广播每一份数据到一组并行工作的进程中的一个,避免在共享 Hash 表的情况下出现数据重复。
底层的扫描由 Seq Scan 变为 Parallel Seq Scan。
并行扫描原理
在 PostgreSQL 中,数据以 Heap 表的形式存储。在读取时,通常有顺序扫描(Seq Scan)、索引扫描(Index Scan)和位图扫描(Bitmap scan)三种扫描方式。接下来,我们对以上三种扫描方式的并行化进行介绍。
图 2:PostgreSQL Heap 表并行顺序扫描示意图
如图 2 所示,在并行顺序扫描时,两个子进程的快照是统一共享的。进程之间通过原子操作动态获得每次要读取的 Page 范围,避免频繁使用锁从而造成瓶颈。Page 的范围并不固定,会根据数据量和读取进度进行动态调整,使得任务尽量均分在不同进程中,避免木桶效应。
图 3:PostgreSQL 并行索引扫描示意图
使用索引扫描并行化查询时,子进程只需要读取对应索引的 Page,每个进程每次只读取一个索引 Page,再读取 Heap 表数据;如果 Page 为全体可见,可以不读取 Heap 表。
图 4:PostgreSQL 并行位图扫描示意图
并行位图扫描在建立底层索引的 Page 范围时,只有一个进程,按照索引信息(CTID)进行顺序扫描。与 PostgreSQL 使用 leader 进程不同的是,CBDB 在并行化查询时,会通过竞争的方式选择一个 X 进程,X 进程负责建立位图,之后多个 workers 竞争读取数据。
图 5:CloudberryDB AO 表并行化查询示意图
除了 Heap 表之外,CBDB 还引入了 AO 表,用来专门存储以追加方式插入的元组。如图 5 所示,AO 表的并行扫描是通过原子操作获取 Segfile,让各个进程通过竞争的方式读取数据。
并行 Join 实现
并行能力的优化需要从多方面来实现,仅凭优化扫描方式能实现的性能提升有限,Join 的并行化改造是另一个重要方向。
在 PG 中有三种 Join,分别为 Nestloop Join、Merge Join 和 Hash Join,CBDB 对上述三种 Join 均实现了并行化。此外,一大特色是增加了共享内表的 Hash Join(Parallel-aware Hash Join)。
Parallel-aware Hash Join 与 Hash Join 相似,区别在于前者是可以共享的,进程之间相互协作共同建立共享的 Hash 内表。
图 6:Build a Join 并行化实现流程示意图
如图 6 所示,在 PG 非并行的情况下,构建 Join 时从目标对象(Table A、Table B)各选取一条综合代价最低的执行路径,合成 Join relations 路径。在开启并行化后,会在上述情况下增加一条并行化最佳路径,与非并行化路径构建 Join。
数据分布并行化
在 Greenplum 中,数据分布有 Partitioned、Replicated、Bottleneck 三种情况,它们之间可以通过 Motion 改变 Locus 的属性进行互相转化,实现数据重分布,CBDB 也沿用了这一特性。
图 7:CBDB 数据分布特性示意图
在 CBDB 构建 Join 的时候,可以通过改变 Locus 进行数据相容。在转化过程中遵循两个原则:
在 Join 时,要保证数据不重复、不丢失;
要选择代价最小的方式。
与 Greenplum 不同的是,CBDB 在开启并行化之后,新建了三个新的 Locus 并行模式,实现不同的数据分布:HashedWorkers、SegmentGeneralWorkers 和 ReplicatedWorkers。 HashedWorkers Locus 表示数据分布在同一组进程之间是随机的,但是合并后数据分布变成 Hashed Locus。同理,SegmentGeneralWorkers 和 ReplicatedWorkers 也代表了数据在进程间随机,合并后满足各自的分布状态。
此外,CBDB 还实现了并行刷新物化视图、并行 Create Table AS、多阶段并行化 Aggregation/Limit 等。通过以上诸多并行优化措施后,CBDB 性能得到大幅度的提升,在特定场景下甚至可以实现千百倍的查询效率提升,支持企业海量数据的复杂分析需求。
图 8:CBDB 并行化性能曲线图
并行化查询是 CBDB 在研发立项之初就确定的产品方向,我们希望能够通过多线程并行执行来充分释放现代多核大内存的硬件能力,降低包括 IO 以及 CPU 计算在内的处理时间,实现响应时间的大幅下降,更好地提升用户使用体验和业务敏捷度。
版权声明: 本文为 InfoQ 作者【HashData】的原创文章。
原文链接:【http://xie.infoq.cn/article/105be1bcd243ccdb3708c8900】。文章转载请联系作者。
评论