0717-7821348
欢乐彩票官网

欢乐彩票官网

您现在的位置: 首页 > 欢乐彩票官网
欢乐彩票登录-Presto完成原理
2019-12-14 00:58:45

Facebook的数据库房存储在少数大型Hadoop/HDFS集群。Hive是Facebook在几年前专为Hadoop打造的一款数据库房东西。在曾经,Facebook的科学家和剖析师一向依托Hive来做数据剖析。但Hive运用MapReduce作为底层核算结构,是专为批处理规划的。但随着数据越来越多,运用Hive进行一个简略的数据查询或许要花费几分到几小时,明显不能满意交互式查询的需求。Facebook也调研了其他比Hive更快的东西,但它们要么在功用有所约束要么就太简略,以至于无法操作Facebook巨大的数据库房。

2012年开端试用的一些外部项目都不适宜,他们决议自己开发,这便是Presto。2012年秋季开端开发,现在该项目现已在超越 1000名Facebook雇员中运用,运转超越30000个查询,每日数据在1PB等级。Facebook称Presto的功能比Hive要好上10倍多。2013年Facebook正式宣告开源Presto。

本文首要介绍Presto从用户提交SQL到履行的这一个进程,然后测验对Presto完结实时查询的原理进行剖析和总结,终究介绍Presto在美团的运用情况。

Presto架构


Presto查询引擎是一个Master-Slave的架构,由一个Coordinator节点,一个Discovery Server节点,多个Worker节点组成,Discovery Server一般内嵌于Coordinator节点中。Coordin欢乐彩票登录-Presto完成原理ator担任解析SQL句子,生成履行方案,分发履行使命给Worker节点履行。Worker节点担任实践履行查询使命。Worker节点发动后向Discovery Server服务注册,Coordinator从Discovery Server获得能够正常作业的Worker节点。假如装备了Hive Connector,需求装备一个Hive MetaStore服务为Presto供给Hive元信息,Worker节点与HDFS交互读取数据。

Presto履行查询进程简介

已然Presto是一个交互式的查询引擎,咱们最关怀的便是Presto完结低延时查询的原理,我以为主要是下面几个要害点,当然还有一些传统的SQL优化原理,这儿不介绍了。

  1. 彻底根据内存的并行核算
  2. 流水线
  3. 本地化核算
  4. 动态编译履行方案
  5. 当心运用内存和数据结构
  6. 类BlinkDB的近似查询
  7. GC操控

为了介欢乐彩票登录-Presto完成原理绍上述几个关键,这儿先介绍一下Presto履行查询的进程

提交查询

用户运用Presto Cli提交一个查询句子后,Cli运用HTTP协议与Coordinator通讯,Coordinator收到查询恳求后调用SqlParser解析SQL句子得到Statement目标,并将Statement封装成一个QueryStarter目标放入线程池中等候履行。

提交查询

SQL编译进程

Presto与Hive相同,运用Antlr编写SQL语法,语法规矩界说在Statement.g和StatementBuilder.g两个文件中。 如下图中所示从SQL编译为终究的物理履行方案大约分为5部,终究生成在每个Worker节点上运转的LocalExecutionPlan,这儿不具体介绍SQL解析为逻辑履行方案的进程,经过一个SQL句子来了解查询方案生成之后的核算进程。

SQL解析进程


样例SQL:

select c1.rank, count(*) from dim.city c1 join dim.city c2 on c1.id = c2.id where c1.id > 10 gr欢乐彩票登录-Presto完成原理oup by c1.rank limit 10;

逻辑履行方案


上面的SQL句子生成的逻辑履行方案Plan如上图所示。那么Presto是怎么对上面的逻辑履行方案进行拆分以较高的并行度去履行完这个方案呢,咱们来看看物理履行方案。

物理履行方案

逻辑履行方案图中的虚线便是Presto对逻辑履行方案的切分点,逻辑方案Plan生成的SubPlan分为四个部分,每一个SubPlan都会提交到一个或许多个Worker节点上履行。

SubPlan有几个重要的特点planDistribution、outputPartitioning、partitionBy特点。

  1. PlanDistribution表明一个查询Stage的分发办法,逻辑履行方案图中的4个SubPlan共有3种不同的PlanDistribution办法:Source表明这个SubPlan是数据源,Source类型的使命会依照数据源巨细确认分配多少鸭嘴兽个节点进行履行;Fixed表明这个SubPlan会分配固定的节点数进行履行(Config装备中的query.initial-hash-partitions参数装备,默许是8);None表明这个SubPlan只分配到一个节点进行履行。鄙人面的履行方案中,SubPlan1和SubPlan0 PlanDistribution=Source,这两个SubPlan都是供给数据源的节点,SubPlan1一切节点的读取数据都会发向SubPlan0的每一个节点;SubPlan2分配8个节点履行终究的聚合操作;SubPlan3只担任输出终究核算完结的数据。
  2. OutputPartitioning特点只要两个值HASH和NONE,表明这个SubPlan的输出是否依照partitionBy的key值对数据进行Shuffle。鄙人面的履行方案中只要SubPlan0的OutputPartitioning=HASH,所以SubPlan2接收到的数据是依照rank字段Partition后的数据。

物理履行方案


彻底根据内存的并行核算

查询的并行履行流程

Presto SQL的履行流程如下图所示

  1. Cli经过HTTP协议提交SQL查询之后,查询恳求封装成一个SqlQueryExecution目标交给Coordinator的SqlQueryManager#queryExecutor线程池去履行
  2. 每个SqlQueryExecution线程(图中Q-X线程)发动后对查询恳求的SQL进行语法解析和优化并终究生成多个Stage的SqlStageExecution使命,每个SqlStageExecution使命依然交给相同的线程池去履行
  3. 每个SqlStageExecution线程(图中S-X线程)发动后每个Stage的使命按PlanDistribution特点结构一个或许多个RemoteTask经过HTTP协议分配给远端的Worker节点履行
  4. Worker节点接收到RemoteTask恳求之后,发动一个SqlTaskExecution线程(图中T-X线程)将这个使命的每个Split包装成一个PrioritizedSplitRunner使命(图中SR-X)交给Worker节点的TaskExecutor#executor线程池去履行

查询履行流程


上面的履行方案实践履行作用如下图所示。

  1. Coordinator经过HTTP协议调用Worker节点的 /v1/task 接口将履行方案分配给一切Worker节点(图中蓝色箭头)
  2. SubPlan1的每个节点读取一个Split的数据并过滤后将数据分发给每个SubPlan0节点进行Join操作和Partial Aggr操作
  3. SubPlan1的每个节点核算完结后按GroupBy Key的Hash值将数据分发到不同的SubPlan2节点
  4. 一切SubPlan2节点核算完结后将数据分发到SubPlan3节点
  5. SubPlan3节点核算完结后告诉Coordinator完毕查询,并将数据发送给Coordinator

履行方案核算流程


源数据的并行读取

在上面的履行方案中SubPlan1和SubPlan0都是Source节点,其实它们读取HDFS文件数据的办法便是调用的HDFS InputSplit API,然后每个InputSplit分配一个Worker节点去履行,每个Worker节点分配的InputSplit数目上限是参数可装备的,Config中的query.max-pending-splits-per-node参数装备,默许是100。

分布式的Hash聚合

上面的履行方案在SubPlan0中会进行一次Partial的聚合核算,核算每个Worker节点读取的部分数据的部分聚合成果,然后SubPlan0的输出会依照group by字段的Hash值分配不同的核算节点,终究SubPlan3兼并一切成果并输出

流水线

数据模型

Presto中处理的最小数据单元是一个Page目标,Page目标的数据结构如下图所示。一个Page目标包括多个Block目标,每个Block目标是一个字节数组,存储一个字段的若干行。多个Block横切的一行是实在的一行数据。一个Page最大1MB,最多16*1024行数据。

数据模型

节点内部流水线核算

下图是一个Worker节点内部的核算流程图,左边是使命的履行流程图。

Worker节点将最细粒度的使命封装成一个PrioritizedSplitRunner目标,放入pending split优先级行列中。每个

Worker节点发动一定数意图线程进行核算,线程数task.shard.max-threads=availableProcessors() * 4,在config中装备。

每个闲暇的线程从行列中取出一个PrioritizedSplitRunner目标履行,假如履行完结一个周期,超越最大履行时间1秒钟,判别使命是否履行完结,假如完结,从allSplits行列中删去,假如没有,则放回pendingSplits行列中。

每个使命的履行流程如下图右侧,顺次遍历一切Operator,测验从上一个Operator取一个Page目标,假如获得的Page不为空,交给下一个Operator履行。

节点内部流水线核算


节点间流水线核算

下图是ExchangeOperator的履行流程图,ExchangeOperator为每一个Split发动一个HttpPageBufferClient目标,自动向上一个Stage的Worker节点拉数据,数据的最小单位也是一个Page目标,取到数据后放入Pages行列中

节点间流水线核算


本地化核算

Presto在挑选Source使命核算节点的时分,关于每一个Split,按下面的战略挑选一些minCandidates

  1. 优先挑选与Split同一个Host的Worker节点
  2. 假如节点不行优先挑选与Split同一个Rack的Worker节点
  3. 假如节点还不行随机挑选其他Rack的节点

关于一切Candidate节点,挑选assignedSplits最少的节点。

动态编译履行方案

Presto会将履行方案中的ScanFilterAndProjectOperator和FilterAndProjectOperator动态编译为Byte Code,并交给JIT去编译为native代码。Presto也运用了Google Guava供给的LoadingCache缓存生成的Byte Code。

动态编译履行方案

动态编译履行方案

上面的两段代码片段中,榜首段为没有动态编译前的代码,第二段代码为动态编译生成的Byte Code反编译之后复原的优化代 码,咱们看到这儿采用了循环展开的优化办法。

循环展开最常用来下降循环开支,为具有多个功用单元的处理器供给指令级并行。也有利于指令流水线的调度。

当心运用内存和数据结构

运用Slice进行内存操作,Slice运用Unsafe#copyMemory完结了高效的内存复制,Slice库房参阅:https://github.com/airlift/slice

Facebook工程师在另一篇介绍ORCFile优化的文章中也说到运用Slice将ORCFile的写功能提高了20%~30%,参阅:https://code.facebook.com/posts/229861827208629/scaling-the-facebook-data-warehouse-to-300-pb/

类BlinkDB的近似查询

为了加速avg、count distinct、percentile等聚合函数的查询速度,Presto团队与BlinkDB作者之一Sameer Agarwal协作引入了一些近似查询函数approx_avg、approx_distinct、approx_percentile。approx_distinct运用HyperLogLog Counting算法完结。

GC操控

Presto团队在运用hotspot java7时发现了一个JIT的BUG,当代码缓存快要到达上限时,JIT或许会停止作业,然后无法将运用频率高的代码动态编译为native代码。

Presto团队运用了一个比较Hack的办法去处理这个问题,添加一个线程在代码缓存到达70%以上时进行显式GC,使得现已加载的Class从perm中移除,防止JIT无法正常作业的BUG。