Presto包含三类角色,coordinator,discovery,worker。coordinator负责query的解析和调度。discovery负责集群的心跳和角色管理。worker负责执行计算。
presto-cli提交的查询,实际上是一个http POST请求。查询请求发送到coordinator后,经过词法解析和语法解析,生成抽象语法树,描述查询的执行。
执行计划编译器,会根据抽象语法树,层层展开,把语法树所表示的结构,转化成由单个操作所组成的树状的执行结构,称为逻辑执行计划。
原始的逻辑执行计划,直接表示用户所期望的操作,未必是性能最优的,在经过一系列性能优化和转写,以及分布式处理后,形成最终的逻辑执行计划。这时的逻辑执行计划,已经包含了map-reduce操作,以及跨机器传输中间计算结果操作。
scheduler从数据的meta上获取数据的分布,构造split,配合逻辑执行计划,把对应的执行计划调度到对应的worker上。
在worker上,逻辑执行计划生成物理执行计划,根据逻辑执行计划,会生成执行的字节码,以及operator列表。operator交由执行驱动来完成计算。
由语法解析器根据SQL,解析生成的树状结构,描述SQL的执行过程。 在下文中,以SQLselect avg(response_size) as a , client_address from localfile.logs.http_request_log group by client_address order by a desc limit 10
为例来描述。
抽象语法树数以Query为单位来描述查询,分层次表示不同层的子查询。每一层查询查询包含了几个关键因素:select
, from
,where
,group by
,having
,order by
,limit
。其中,from
可以是一个子查询,也可以是一张表。
一个典型的抽象语法树:
抽象语法树树,描述的最原始的用户需求。抽象语法树描述的信息,执行效率上不是最优,执行操作也过于复杂。需要把抽象语法树转化成执行计划。执行计划分成两类,一类是逻辑执行计划,一类是物理执行计划。逻辑执行计划,以树状结构来描述执行,每个节点是最简单的操作。物理执行计划,根据逻辑执行计划生成字节码,交由驱动执行。
转写成逻辑执行计划的过程,包括转写和优化。把抽象语法树转写成由简单操作组成的结点树,然后把树中所有聚合计算节点转写成map-reduce形式。并且在map-reduce节点中间插入Exchange节点。然后,进行一系列优化,把一些能提前加速计算的节点下推,能合并的节点合并。
最后逻辑执行计划按照Exchange节点做划分,分成不同的段(fragament),表示不同阶段的的执行计划。在调度时,按照fragment调度。
SQLselect avg(response_size) as a , client_address from localfile.logs.http_request_log group by client_address order by a desc limit 10
的逻辑执行计划:
从执行计划中可以看到,agg节点都是分成partial和final两步。
调度涉及到两个问题,第一,某个fragment分配由哪些机器执行;第二,某个fragment的计算结果如何输出到下游fragment。
在调度时,需要为每一个fragment指定分配到哪些机器上。从调度上划分,fragment分三种类型
hash_partition_count
决定。选择机器的方式是随机选择。对于计算结果输出,根据下游节点的机器个数,也有多种方式,
以下图为例,fragment 2是source类型fragment,有三个split,所以分配了三台机器。因为这一层计算是group by 聚合计算,所以输出时按照group by的key计算hash,选择下游的某台机器输出。
调度之前的任务,都在coordinator完成,调度完成后,之后任务发送到worker上执行。
逻辑执行计划fragment发送到机器上后,由结点树形式转写成operator list,根据逻辑代码动态编译生成字节码。动态生成字节码,主要是利用编译原理:
这些手段会更好的利用CPU的流水线。
物理执行计划构造生成的Operator list,交给Driver执行。具体计算哪些数据,由加载的Split决定。
Operator list 以串联形式处理数据,前一个operator的结果作为下一个结果的输入,对于source类型的operator,每一次调用都会获取一份新的数据;对于Aggregate的operator,只有之前所有的operator都finish之后,才能获取输出结果。
生成的执行计划中,聚合计算都拆分成了两步,分别是Map、Reduce。
聚合计算的Operator有两类,分别是AggregationOperator和HashAggregationOperator。
AggregationOperator对所有行进行计算,并且把结果更新到一个位置。HashAggregationOperator使用某一列的hash值作为hash表的key,key相同的行才会把结果保存在一起,用于group by类的计算。
聚合计算都是要按照Map-Reduce的形式执行。
聚合计算所提供的函数,都要提供四个接口,分别有两个输入,两个输出:
1+3 构成了Map操作 2+4构成了Reduce操作。
以Avg为例:
我们改造了Presto系统,使得Presto能够提供缓存功能,就是在MapReduce中间加了一层计算,接受中间结果输入和中间结果输出。
函数分为两类,分别是Scaler函数和Aggregate函数
Scaler函数提供数据的转换处理,不保存状态,一个输入产生一个输出。
Aggregate函数提供数据的聚合处理,利用已有状态+输入,产生新的状态。
上文已经提到,聚合函数提供了两种输入接口和两种输出接口。
招聘阿里云智能事业群-智能日志分析研发专家-杭州/上海 扫码加我