揭秘!Greenplum并行执行引擎到底是如何工作的?

68b96773-2749-4288-bff8-b30016081c6c.gif



《深入浅出Greenplum内核》系列直播以每月一场的速度持续推出中。在第一场《架构解读》直播里,我们了解了Greenplum的整体架构、存储管理、索引、查询执行、事务与日志等内容。今天(5月22日)第二场《Greenplum内核揭秘之执行引擎》也顺利播出啦!现在,我们回顾一下直播演讲内容吧!

看完别忘了前往askGP做一下小测试(ask.greenplum.cn/exam)巩固一下所学的知识点哦!ffe2b0dc-3afc-4ef4-b287-f3f4dcad8967.png



感谢大家参加Greenplum的直播活动!在《深入浅出Greenplum内核》系列活动的第二场直播中,我们为大家详细介绍了Greenplum执行器内容,以及Greenplum为了完成执行计划而设计的两个特殊模块Dispatcher和Interconnect等内容。


执行器


首先我们先来了解一下什么是执行器。简单来讲,执行器是处理一个由执行计划节点组成的树,并返回查询结果。那么什么是执行计划节点呢?从本质上讲,一个执行计划节点,实际上就是一个数据处理节点。从下图可看到,在数据输入后,执行节点会对数据进行数据处理,然后返回数据作为输出。这些执行节点会被组织成树的形式。


adc20e3b-7aff-4d5d-b40f-8fa4a325df52.png


下图是一个SELECT查询的执行计划树。通过优化器优化后,就会生成这样的树状结构,我们可以看到里面有四个执行节点,包括HashJoin节点,Hash节点,顺序扫描节点,所有的节点通过树的方式组织在一起,来表示各节点之间的数据流动或者顺序关系。  每一个计划节点包含足够多的元数据信息提供给执行器。

 

图中的Seq Scan被称为原发性的扫描节点,原发性的扫描节点是指,节点本身可以自己产生数据,而不依赖于其他节点;反之,非原发性扫描节点是需要子节点来为其提供数据,图中的Hash Join和Hash就是非原发性扫描节点。了解了原发性扫描节点和非原发性扫描节点的不同,就可以更好的理解后面的执行模型。


e2c022f5-4737-40ae-809d-22d283905969.png


那么执行器是怎么执行生成的执行计划树呢?就需要利用执行模型了。 面对这样的执行计划树时,处理方式其实很多,我们会根据包括每一个节点内的数据输入是怎么样的规定,输出有什么样的特点等不同的信息,会选择不同的执行模型。
现在我们来介绍一下几种常见的执行模型。

执行模型



第一种是迭代模型,也被称为流式模型,或者是抽拉式模型 。它的定义非常简单,每一个执行节点本质上就是一个next函数,我们会从一个树节点的根节点一直往下执行这个next 函数。next 函数的实现会遵循这样的特点:
  • 从输出角度看,next 函数的每一次调用,执行节点返回一个tuple,没有更多tuple的时候返回一个NULL。

  • 从输入的角度看,执行节点实现一个循环,每次调用子执行节点的next函数来获取它们的输出,并处理它们直到能返回一个tuple或者NULL。

  • 执行控制流方向是自上往下,不断抽拉的方式,由上层节点直接驱动下层节点来进行数据的驱动。而从数据流的角度来看,还是由上层节点往下层节点传输来完成。


这种执行模型的有点在于规则简单,易懂,资源使用少,通用性好,大部分的执行计划节点一般都可以用这种模式来实现。缺点也很显而易见,由于每次迭代只返回一个tuple,迭代次数多,代码局部性较差,同时对CPU cacheline也不是很友好。


e8f54e1c-023e-4160-8f1b-04e9c725c436.png


向量化模型



第二种模型就是向量化模型 ,和迭代模型有一些相似之处,比如每一个执行节点实现一个next函数,但也有其不同之处。每一次迭代,执行节点返回一组tuple而非一个tuple,从而减少迭代次数,可以利用新的硬件特性如SIMD来加快一组tuple的处理。同时一组tuple在不同的节点之间传输,对列存也更加友好。
执行节点实现一个循环,每次调用子执行节点的next函数来获取它们的输出,并能够批量的处理数据。执行控制流方向自上而下,采用pull的方式。


a28e6efd-6a38-4d89-a857-cff67851c991.png


Push执行模型




第三种模型是目前比较热门的模型——PUSH执行模型。 每一个执行节点定义两个函数
  • Produce函数


Produce函数:看起来像是一个执行节点tuple的生产函数,其实不然,对于非自主生产的执行节点,produce函数更像一个控制函数,它不做过多的生产的工作,想反它会立即调用子节点的produce函数。具有自主生产的执行节点(一般为叶子节点),其produce函数名副其实的生产tuple,并驱动父节点的consume函数提取数据。
  • Consume函数


Consume函数:被下层节点驱动调用,接收子节点数据,进行各种运算,并驱动其父节点的consume函数。


现在我们通过一个例子来看一下,下图中有三个节点,一个扫描节点,一个投影节点,一个Join 节点。每个节点都生成了两个函数,一个生产函数,一个消费函数。整个PUSH模型是怎么做的呢?图中的红框标注的为原发性的扫描节点,蓝框标注的是非原发性的扫描节点。非原发性的扫描节点中的生产函数并不做真正的生产工作,而更多是承担了控制工作,会调用它的子节点的生产函数。因此投影节点和Join节点会调用scan的生产函数。由于Scan是原发性的,因此会在生产并得到数据后,开始驱动数据的消耗。


7f0dbda2-027d-4aad-8895-a0886f40356f.png


PUSH模型是由下层的节点驱动上层的节点来完成的。数据流向也是自下而上的。下层驱动模型可以相对容易的转换成由数据驱动的代码。好处就是,上层的操作就会变成本节点的算子,增加代码的局部性。此外,这样的代码可以更方便进一步转换为一个纯计算代码,例如使用LLVM优化等。个人认为这种模型通用性不强,只能做一些局部的优化。


Greenplum使用的是迭代模型,但我们正在积极探索向量化模型和PUSH模型。Greenplum正在开发相应的功能,并提交到PG社区,基本思路是利用custom scan 的可定制特性,实现向量化版本的AGG节点,SORT节点,并替换原有查询执行树中的相应节点。大家对这一块感兴趣也欢迎去相应的邮件列表查看。


  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值