您好,登录后才能下订单哦!
小编给大家分享一下PostgreSQL如何实现并行查询,希望大家阅读完这篇文章之后都有所收获,下面让我们一起去探讨吧!
随着SSD等磁盘技术的平民化,以及动辄上百GB内存的普及,I/O层面的性能问题得到了有效缓解。提升数据库的扩展性能,可以追求Scale Out的方式,增加机器,往分布式方向发展,也可以追求Scale Up,增加硬件组件,充分利用各个硬件的资源,把单机的性能发挥到最大效果。相较而言,Scale Up通过软件加速性能,依赖软件层面的优化,是低成本的扩展方案。
现代服务器除了磁盘和内存资源的增强,多CPU的配置也足够强大。数据库的Join、聚合等操作内存耗费比较大,很多时间花在了数据的交换和缓存上,CPU的利用率并不高,所以面向CPU的加速策略中,并发执行是一种常见的方法。
查询的性能是评价OLAP型数据库产品好坏的核心指标,而并行查询可以聚焦在数据的读和计算上,通过把Join、聚合、排序等操作分解成多个操作实现并行。
并行查询的挑战在于,为了要做并行而加入的数据分片过程、进程或线程间的通信,以及并发控制方面带来的系统开销不但没有增加性能,反而降低了原有性能。实现上,如何在优化器里规划好并行计划也是很多数据库做不到的。
PostgreSQL的并行查询功能主要由PostgreSQL社区的核心开发者Robert Haas等人开发。从Robert Haas的个人博客了解到,社区开发PostgreSQL的并行查询特性时间表如下:
2013年10月,执行框架上做了Dynamic Background Workers和Dynamic Shared Memory两个调整
2014年12月,Amit Kapila提交了一个简单版的parallel sequential scan的patch;
2015年3月,正式版的parallel sequential scan的patch被提交;
2016年3月,支持parallel joins和parallel aggregation;
2016年4月,作为9.6的新特性发布。
PostgreSQL的并行查询在大数据量(中间结果在GB以上)的Join、Merge场合,效果比较明显。效果上,因为系统开销,投入的资源跟性能提升并不是线性的,比如增加4个worker,性能则可能提升2倍左右,而不是4倍。通过TPCH的测试效果,表明在Ad-Hoc查询场景,普遍都有加速效果。
现在支持的并行场景主要是以下3种:
parallel sequential scan
parallel join
parallel aggregation
鉴于安全考虑,以下4种场景不支持并行:
公共表表达式(CTE)的扫描
临时表的扫描
外部表的扫描(除非外部数据包装器有一个IsForeignScanParallelSafeAPI)
对InitPlan或SubPlan的访问
使用并行查询,还有以下限制:
必须保证是严格的read only模式,不能改变database的状态
查询执行过程中,不能被挂起
隔离级别不能是SERIALIZABLE
不能调用PARALLEL UNSAFE函数
并行查询有基于代价策略的判断,譬如小数据量时默认还是普通执行。在PostgreSQL的配置参数中,提供了一些跟并行查询相关的参数。我们想测试并行,一般设置下面两个参数:
force_parallel_mode:强制开启并行模式的开关
max_parallel_workers_per_gather:设定用于并行查询的worker进程数
一个简单的两表Join查询场景,使用并行查询模式的查询计划如下:
并行查询开启后,解析器会生成一份Gather…Partial风格的执行计划,这意味着到Executor层,会将Partial部分的计划并行执行。
执行计划里可以看到,在做并行查询时,额外创建了2个worker进程,加上原来的master进程,总共3个进程。Join的驱动表数据被平均分配了3份,通过并行scan分散了I/O操作,之后跟大表数据分别做Join。
PostgreSQL的并行由多个进程的机制完成。每个进程在内部称之为1个worker,这些worker可以动态地创建、销毁。PostgreSQL在SQL语句解析和生成查询计划阶段并没有并行。在执行器(Executor)模块,由多个worker并发执行被分片过的子任务。即使在查询计划被并行执行的环节,一直存在的进程也会充当一个worker来完成并行的子任务,我们可以称之为主进程。同时,根据配置参数指定的worker数,再启动n个worker进程来执行其他子计划。
PostgreSQL内延续了共享内存的机制,在每个worker初始化时就为每个worker分配共享内存,用于worker各自获取计划数据和缓存中间结果。这些worker间没有复杂的通信机制,而是都由主进程做简单的通信,来启动和执行计划。
PostgreSQL中并行的执行模型如图1所示。
以上文的Hash Join的场景为例,在执行器层面,并行查询的执行流程如图2所示。
各worker按照以下方式协同完成执行任务:
首先,每个worker节点做的任务相同。因为是Hash Join,worker节点使用一个数据量小的表作为驱动表,做Hash表。每个worker节点都会维护这样一个Hash表,而大表被平均分之后跟Hash表做数据Join。
最底层的并行是磁盘的并行scan,worker进程可以从磁盘block里获取自己要scan的block。
Hash Join后的数据是全部数据的子集。对于count()这种聚合函数,数据子集上可以分别做计算,最后再合并,结果上可以保证正确。
数据整合后,做一次总的聚合操作。
worker进程又是如何创建和运行的?首先来看worker的创建逻辑(参见图3)。
PostgreSQL的并行处理,以worker动态创建为前提。worker可以由主进程初始化出来,并且在上下文中,先指定好入口函数。
并行查询中,入口函数被指定为ParallelWorkerMain。而ParallelWorkerMain函数里,在完成一系列信号代理设定后,会调用ParallelQueryMain来执行查询。ParallelQueryMain创建了一个新的执行器上下文,递归执行并行子查询计划。
用来并行查询的worker进程接收主进程的信号,比如一旦发送创建进程的信号,worker进程就会启动,紧接着执行ParallelWorkerMain函数。进而,ParallelQueryMain也会执行,各个worker进程独立执行子计划,执行结果会存在共享内存里。所有进程执行结束后,master进程会去搜集共享内存里的结果数据(tuple),做数据整合。
并行查询的特性公布后,不乏对并行的评价和之后的改进计划。社区并行查询的开发者在博客中提到准备做一个大的共享Hash Table,这样Hash Join操作的并行度会进一步提升。
另外,对PostgreSQL而言,反倒是基于其folk出来的一些数据库产品先于它做了并行查询的特性,可以学习参考:
Postgres-XC的分布式框架
GreenPlum的MPP架构
CitusDB的分布式
VitesseDB基于多线程的并行
Fujitsu的Fujitsu Enterprise PostgreSQL的并行
其中开源数据库GreenPlum并行架构很有借鉴意义。GreenPlum的并行查询设计了一个专门的调度器来协调查询任务的分配,而PostgreSQL没有这样的设计。关于GreenPlum的执行框架,简单讲是以下三层结构:
调度器(QD):调度器发送优化后的查询计划给所有数据节点(Segments)上的执行器(QE)。调度器负责任务的执行,包括执行器的创建、销毁、错误处理、任务取消、状态更新等。
执行器(QE):执行器收到调度器发送的查询计划后,开始执行自己负责的那部分计划。典型的操作包括数据扫描、哈希关联、排序、聚集等。
Interconnect:负责集群中各个节点间的数据传输
GreenPlum会根据数据分布情况做数据的广播和重分布,这是PostgreSQL的并行模型可以借鉴的。
仅仅是一个大的Hash Table,在数据访问上有串行的开销,worker的并行仍然受限。如图5所示,大表和小表Join的场景参考GreenPlum的数据广播机制,驱动表的数据可以给每个worker进程准备一个拷贝,相当于广播了一份数据。这样数据被高度共享,并行的效果会更好。
除了PostgreSQL生态的数据库,关系型数据库老大哥Oracle在并行查询上已经积累了30年的经验,也需要借鉴。在Oracle的官方手册中,有对其并行查询机制做出的说明。
Oracle在每个操作环节,都能把数据高度分片,可以参考图6所示的Hash Join的并行。
而在内部并行控制上,数据被分组后,不管是scan还是排序,几组worker对分组的数据都能分治。
也就是说Oracle做到了操作符(Operator)Level的并行。在每个操作中,把数据分片后动态的并行运算。可以看到Oracle的并行查询在做Operator级别的并行,每个操作环节,都能把数据分片后分而治之,并行程度非常高。这对数据的流转要求也很高,数据和操作既能水平分治也能垂直分治。
PostgreSQL目前是任务级别的并行,将原先的执行计划垂直拆分成几个可以分离的子任务,并行实现简单,但在大数据量时并行度不够,而且共享内存的访问负荷加重,性能提升不明显。
参考Oracle的方式,按上图改进后,worker不再是单独执行1个任务,而是随时被调用执行操作。数据根据操作分层、分片、广播,worker进程为数据操作服务,而不是数据为worker服务。这样在超大规模数据的场景,驱动表作为producer做数据partition,外表作为consumer做operator运算。多组这样的操作产生的并行计算更自由,性能也更有想象空间,也是我们团队目前在尝试的方向。
看完了这篇文章,相信你对“PostgreSQL如何实现并行查询”有了一定的了解,如果想了解更多相关知识,欢迎关注亿速云行业资讯频道,感谢各位的阅读!
免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。