PostgreSQL并行查询介绍

栏目: 数据库 · 发布时间: 8年前

内容简介:PostgreSQL并行查询介绍

导语 】2016年4月,PostgreSQL社区发布了PostgreSQL 9.6 Beta 1,迎来了并行查询(Parallel Query)这个新特性。在追求高性能计算和查询的大数据时代,能提升性能的特性都会成为一个新的热门话题。作为关注PostgreSQL发展的数据库开发者,本方作者将分享对于一些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查询场景,使用并行查询模式的查询计划如下:

test=# select count(*) from t1;
  count   
----------
 10,000,000
(1 row)
test=# select count(*) from t2;
  count   
----------
 10,000,000
(1 row)
test=# explain analyze  select count(*) from t1,t2  where t1.id = t2.id ;
                           QUERY PLAN                                                
------------------------------------------------------------------------------
 Finalize Aggregate  (cost=596009.38..596009.39 rows=1 width=8) (actual time=17129.158..17129.158 rows=1 loops=1)
   ->  Gather  (cost=596009.17..596009.38 rows=2 width=8) (actual time=16907.462..17129.132 rows=3 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         ->  Partial Aggregate  (cost=595009.17..595009.18 rows=1 width=8) (actual time=17038.230..17038.231 rows=1 loops=3)
               ->  Hash Join  (cost=308310.48..570009.22 rows=9999977 width=0) (actual time=8483.284..16703.813 rows=3333333 loops=3)
                     Hash Cond: (t1.id = t2.id)
                     ->  Parallel Seq Scan on t1  (cost=0.00..85914.87 rows=4166687 width=4) (actual time=0.575..741.057 rows=3333333 loops=3)
                     ->  Hash  (cost=144247.77..144247.77 rows=9999977 width=4) (actual time=8449.743..8449.743 rows=10000000 loops=3)
                           Buckets: 131072  Batches: 256  Memory Usage: 2400kB
                           ->  Seq Scan on t2  (cost=0.00..144247.77 rows=9999977 width=4) (actual time=0.294..2177.531 rows=10000000 loops=3)

并行查询开启后,解析器会生成一份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所示。

PostgreSQL并行查询介绍

图1 PostgreSQL并行查询的框架

以上文的Hash Join的场景为例,在执行器层面,并行查询的执行流程如图2所示。

PostgreSQL并行查询介绍

图2 并行查询的执行流程

各worker按照以下方式协同完成执行任务:

  • 首先,每个worker节点做的任务相同。因为是Hash Join,worker节点使用一个数据量小的表作为驱动表,做Hash表。每个worker节点都会维护这样一个Hash表,而大表被平均分之后跟Hash表做数据Join。

  • 最底层的并行是磁盘的并行scan,worker进程可以从磁盘block里获取自己要scan的block。

  • Hash Join后的数据是全部数据的子集。对于count()这种聚合函数,数据子集上可以分别做计算,最后再合并,结果上可以保证正确。

  • 数据整合后,做一次总的聚合操作。

worker进程又是如何创建和运行的?首先来看worker的创建逻辑(参见图3)。

PostgreSQL并行查询介绍

图3 PostgreSQL的worker创建

PostgreSQL的并行处理,以worker动态创建为前提。worker可以由主进程初始化出来,并且在上下文中,先指定好入口函数。

并行查询中,入口函数被指定为ParallelWorkerMain。而ParallelWorkerMain函数里,在完成一系列信号代理设定后,会调用ParallelQueryMain来执行查询。ParallelQueryMain创建了一个新的执行器上下文,递归执行并行子查询计划。

用来并行查询的worker进程接收主进程的信号,比如一旦发送创建进程的信号,worker进程就会启动,紧接着执行ParallelWorkerMain函数。进而,ParallelQueryMain也会执行,各个worker进程独立执行子计划,执行结果会存在共享内存里。所有进程执行结束后,master进程会去搜集共享内存里的结果数据(tuple),做数据整合。

并行查询的改进

并行查询的特性公布后,不乏对并行的评价和之后的改进计划。社区并行查询的开发者在博客中提到准备做一个大的共享Hash Table,这样Hash Join操作的并行度会进一步提升。

PostgreSQL并行查询介绍

图4 创建大的Hash表共享数据

另外,对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的官方手册中,有对其并行查询机制做出的说明。

PostgreSQL并行查询介绍

图5 借鉴GreenPlum的广播机制提升并行效果

Oracle在每个操作环节,都能把数据高度分片,可以参考图6所示的Hash Join的并行。

PostgreSQL并行查询介绍

图6 Oracle的Hash Join操作的并行流程

而在内部并行控制上,数据被分组后,不管是scan还是排序,几组worker对分组的数据都能分治。

也就是说Oracle做到了操作符(Operator)Level的并行。在每个操作中,把数据分片后动态的并行运算。可以看到Oracle的并行查询在做Operator级别的并行,每个操作环节,都能把数据分片后分而治之,并行程度非常高。这对数据的流转要求也很高,数据和操作既能水平分治也能垂直分治。

PostgreSQL目前是任务级别的并行,将原先的执行计划垂直拆分成几个可以分离的子任务,并行实现简单,但在大数据量时并行度不够,而且共享内存的访问负荷加重,性能提升不明显。

PostgreSQL并行查询介绍

图7 Oracle内部动态的并行操作

参考Oracle的方式,按上图改进后,worker不再是单独执行1个任务,而是随时被调用执行操作。数据根据操作分层、分片、广播,worker进程为数据操作服务,而不是数据为worker服务。这样在超大规模数据的场景,驱动表作为producer做数据partition,外表作为consumer做operator运算。多组这样的操作产生的并行计算更自由,性能也更有想象空间,也是我们团队目前在尝试的方向。

PostgreSQL并行查询介绍

图8 通过数据分组和worker分组提升PostgreSQL的并行

笔者对数据库实现的理解深度有限,立足自己的经验分享了关于并行查询的以上认识。关注社区邮件,可以看到PostgreSQL社区非常积极地加入更多并行查询的特性,比如parallel bitmap index等,相信并行查询的特性会更丰富。期待后面越来越强大的并行计算,以及随之而来性能加速的无限可能。

作者:赵志强,南京SkyData(天数科技)数据库团队的负责人,2008年开始从事数据库的开发工作。2016年加入SkyData初创团队,研发数据平台产品。目前主要关注数据库等基础软件的加速技术,以及时序数据库的开发与应用。

责编:仲培艺,关注数据库领域,纠错、寻求报道或投稿请致邮zhongpy@csdn.net。

本文为《程序员》原创文章,未经允许不得转载,更多精彩文章请 订阅2017年《程序员》


以上所述就是小编给大家介绍的《PostgreSQL并行查询介绍》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Concepts, Techniques, and Models of Computer Programming

Concepts, Techniques, and Models of Computer Programming

Peter Van Roy、Seif Haridi / The MIT Press / 2004-2-20 / USD 78.00

This innovative text presents computer programming as a unified discipline in a way that is both practical and scientifically sound. The book focuses on techniques of lasting value and explains them p......一起来看看 《Concepts, Techniques, and Models of Computer Programming》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

Markdown 在线编辑器
Markdown 在线编辑器

Markdown 在线编辑器

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换