presto原理

presto 背景

  MapReduce不能满足大数据快速实时adhoc查询计算的性能要求,Facebook2012年开发,2013年开源

  基于内存的并行计算,Facebook推出的分布式SQL交互式查询引擎 多个节点管道式执行支持任意数据源 数据规模GB~PB 是一种Massively parallel processing(mpp)(大规模并行处理)模型
数据规模PB 不是把PB数据放到内存,只是在计算中拿出一部分放在内存、计算、抛出、再拿

presto 的应用场景

  • ETL: Presto 支持多种数据源,通过sql将一个数据源中的数据导入到另一个数据源中
  • 实时计算: Presto 性能优越,是实时查询工具上的一个重要的选择
  • Ad-Hoc 用户根据自己的需求随时的调整和选择查询条件,presto根据用户的查询条件返回查询结果并生成报表
  • 实时数据流分析:Presto -Kafka 使用sql语句对kafka中的数据流进行清洗,分析和计算

presto的优点

  1. 多数据源、混合计算支持:支持众多常见的数据源,并且可以进行混合计算分析;
  2. 大数据:完全的内存计算,支持的数据量完全取决于集群内存大小。他不像SparkSQL可以配置把溢出的数据持久化到磁盘,Presto是完完全全的内存计算;
  3. 高性能:低延迟高并发的内存计算引擎,相比Hive(无论MR、Tez、Spark执行引擎)、Impala 执行效率要高很多。根据Facebook和京东的测试报告,至少提升10倍以上;
  4. 支持ANSI SQL:这点不像Hive、SparkSQL都是以HQL为基础(方言),Presto是标准的SQL。用户可以使用标准SQL进行数据查询和分析计算;
  5. 扩展性:有众多 SPI 扩展点支持,开发人员可编写UDF、UDTF。甚至可以实现自定义的Connector,实现索引下推,借助外置的索引能力,实现特殊场景下的 MPP;
  6. 流水线:Presto 是基于PipeLine进行设计,在大量数据计算过程中,终端用户(Driver)无需等到所有数据计算完成才能看到结果。一旦开始计算就可立即产生一部分结果返回,后续的计算结果会以多个Page返回给终端用户(Driver)。

presto 的架构

coordinator

  主要用于接收客户端提交的查询,解析查询语句,执行词法分析生成查询执行计划,并生成Stage 和 Task 进行调度;然后合并结果,把结果返回给客户端(Client);

worker
  • 执行任务的节点
connector

  presto以插件形式对数据存储层进行了抽象,它叫做连接器,不仅包含Hadoop相关组件的连接器还包括RDBMS连接器
具体访问哪个数据源是通过catalog 中的XXXX.properties文件中connector.name决定的
提取数据 负责实际执行查询计划

discovery service

  是将 coordinator 和 worker 结合到一起的服务。Worker 节点启动后向Discovery Server 服务注册,Coordinator 从 Discovery Server 获得可以正常工作的 Worker 节点;

presto 模型

Presto 使用 Catalog、Schema和Table 这3层结构来管理数据
在这里插入图片描述

catalog

相当于MySQL的一个实例,

schema

相当于MySQL的database

presto查询执行模型
  • Statement语句 其实就是输入的SQL
  • Query 根据SQL语句生成查询执行计划,进而生成可以执行的查询(Query),一个查询执行由Stage、Task、Driver、Split、Operator和DataSource组成
  • Stage 执行查询阶段 Stage之间是树状的结构 ,RootStage 将结果返回给coordinator ,SourceStage接收coordinator数据 其他stage都有上下游 stage分为四种 single(root)、Fixed、source、coordinator_only(DML or DDL)
  • Exchange 两个stage数据的交换通过Exchange 两种Exchange ;Output Buffer (生产数据的stage通过此传给下游stage)Exchange Client (下游消费);如果stage 是source 直接通过connector 读数据,则改stage通过Operator与connector交互stage 并不会被执行,只是对执行计划进行管理。
  • Task Stage拆分为一系列的Task,这些Task是实际运行Worker节点的,从而可以并行的执行Stage,每个Task可以拆分一个或者多个Driver,处理一个或者多个Split。
  • Driver Task包含一个或者多个并行Driver,Driver再数据上运行并且结合运算符来生产输出,这些输出稍后会由一个task来进行聚合并传递到另一个Stage的task上,Driver是一个Operator的集合,一个Driver只有一个输入和一个输出。
  • Operator 一个operator代表对一个split的一种操作,列如:过滤,转换.一个Operator一次读取一个Split的数据,将Operator表示的计算和操作作用与Split的数据上,并生产输出,每个Operator会将Page作为最小的单位分别读取输入数据和产生输出数据。
  • Split 分片一个分片就是一个大的数据集中的一个小的子集。
  • Page presto中处理的最小数据单元。 一个page包含多个block对象,每个block对象是个字节数组,存储一个字段的若干行,多个block横切一行是最真是的一行数据,一个Page最大时1M,最多时16*1024行。

工作的原理

在这里插入图片描述

  1. coordinator接到SQL后,通过SQL语法解析器把SQL语法解析变成一个抽象的语法树AST,该树的每一个节点都是一个Node,如:Select部分,Group by 部分,Where 部分,抽象语法树以Query为单位来描述查询,分层次表示不同层次的子查询,只是进行语法解析如果有错误此环节暴露
  2. 抽象语法树是描述的最原始的用户的需求,执行效率上不是最优的,执行操作上也过于复杂,需要将抽象语法树转化和优化执行计划,这就是逻辑执行计划的过程:
    1. 分析语法树的每一个节点,返回列描述符TupleDescriptor供后面使用
    2. 查询表的元数据信息,根据列描述符和目的表元数据,对查询语句进行校验
    3. 生成逻辑算子operator(执行计划节点)
    4. 生成逻辑查询计划
    5. 优化逻辑查询计划
    6. 划分stage
    7. 每个stage 划分为若干个task
  3. 递归遍历抽象语法树,把语法树转成由操作组成的节点树,在shuffle过程的节点加入Exchange节点,然后,通过优化规则进行一系列的优化,就生成了逻辑执行计划.将其分发到分布式的逻辑计划器里,进行分布式解析,最后转化为一个个task
  4. 在每个task里面,会将位置信息解析出来,交给执行的plan(subplan),由plan将task分给worker执行
  5. 执行 Source Stage的Task,这些Task通过connector从数据源中读取一个Split的数据
  6. 处于下游的Stage中的Task会读取上有Stage产生的输出结果,并在该Stage的每个Task所在的Worker的内存中进行后续的计算和处理
  7. Coordinator从分发的Task之后,一致持续不断的从 Singe Stage 中的Task获取计算结果,并将计算结果缓冲到buffer中,直到获取到所有的计算结果
  8. client从提交查询结果后,就会不厅的从Coordinator中获取本次查询的计算结果,直到获取到所有的查询结果
执行计划优化器
  • SetFlatteningOptimizer:用于扁平化union语句执行计划树,即合并能合并union节点,减少执行嘉华树的节点层数
    在这里插入图片描述

  • ProjectionsPushDown: 将UnionNode 之上的ProjectNode的节点下推到UnionNode之下,有利于减少UnionNode所处理节点的列数
    在这里插入图片描述

  • PredicatePushDown:将过滤条件进行下推,减少下层节点处理的数据量,提高执行效率
    在这里插入图片描述

  • LimitPushDown: 将限制条件进行下推,减少下层加典处理的数据的量,提高执行效率
    在这里插入图片描述

执行计划分段SubPlan

经过执行计划进行生成和优化后,最后对执行计划进行分段,按照Exchange节点划分,分成不同段的(fragament)表示不同阶段SubPlan.fragament有几种:

  • Source:从数据源表中读取数据的阶段
  • Fixed:将Source阶段读取的数据分散到各个节点惊醒局部聚合,局部join,局部数据写入等操作
  • Single:只在单节点上进行,用于汇总所有的处理结果的数据,并将结果传递给coordinator
  • Coordinator_only:Coordinator_only阶段只在Coordinator上进行,列入:TableCommitNode这个阶段
Task执行计划图:
  • coordinator将一个个subplan(task)分发给worker
  • SubPlan1 执行Source阶段
  • SubPlan0 执行Fixed阶段局部聚合,局部平均
  • SubPlan2 执行Fixed阶段有shuffle情况下全局聚合
  • SubPlan3 执行Single阶段
    在这里插入图片描述

低延迟原理

  • 基于内存的并行计算

  • 流水式计算作业

    • 节点间流水线计算

      • 下图是ExchangeOperator的执行流程图,ExchangeOperator为每一个Split启动一个HttpPageBufferClient对象,主动向上一个Stage的Worker节点拉取数据,数据的最小单位也是一个Page对象,取到数据后放到Pages队列中
        在这里插入图片描述
    • 节点内部流水线计算

      • Worker节点将最细粒度的任务封装成一个PrioritizedSplitRunner对象,放入pending split优先级队列中。每个
      • Worker节点启动一定数目的线程进行计算,线程数task.shard.max-threads=availableProcessors() * 4,在config中配置。
      • 每个空闲的线程从队列中取出一个PrioritizedSplitRunner对象执行,如果执行完成一个周期,超过最大执行时间1秒钟,判断任务是否执行完成,如果完成,从allSplits队列中删除,如果没有,则放回pendingSplits队列中。
      • 每个任务的执行流程如下图右侧,依次遍历所有Operator,尝试从上一个Operator取一个Page对象,如果取得的Page不为空,交给下一个Operator执行。
        在这里插入图片描述
  • 本地化计算

    • Single阶段:随机选择策略
    • Fixed阶段: 随机选择策略
    • Source阶段:
      • 优先选择与Split同一个Host的Worder节点
      • 次之选择与Split同一个Rack的Worder节点
      • 如果节点都不够,再次之随机选择其他Rack的节点
    • Coordinator_only阶段:Coordinator的节点
  • 动态编译执行计划

    • Presto会将执行计划中的ScanFilterAndProjectOperator和FilterAndProjectOperator动态编译为ByteCode,并交给JIT去编译为native代码。Presto也使用了Google Guava提供的LoadingCache缓存生成的Byte Code。
      在这里插入图片描述

    动态编译执行计划
    在这里插入图片描述

    动态编译执行计划

    • 上面的两段代码片段中,第一段为没有动态编译前的代码,第二段代码为动态编译生成的Byte Code反编译之后还原的优化代 码,我们看到这里采用了循环展开的优化方法。循环展开最常用来降低循环开销,为具有多个功能单元的处理器提供指令级并行。也有利于指令流水线的调度。
  • 小心使用内存和数据结构

    • 使用Slice进行内存操作,Slice使用Unsafe#copyMemory实现了高效的内存拷贝,Facebook工程师在另一篇介绍ORCFile优化的文章中也提到使用Slice将ORCFile的写性能提高了20%~30%,参考
  • 类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。

  • 源数据并行读取

内存管理

内存池

Presto采用逻辑的内存池,来管理不同类型的内存需求。

Presto把整个内存划分成三个内存池,分别是System Pool ,Reserved Pool, General Pool。
在这里插入图片描述

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zlSKJOaU-1633421919317)(EAA0DBD48C134F63ACC71ACD5433AD05)]

  • System Pool 是用来保留给系统使用的,默认为40%的内存空间留给系统使用。
  • Reserved Pool和General Pool 是用来分配query运行时内存的。
  • 其中大部分的query使用general Pool。 而最大的一个query,使用Reserved Pool, 所以ReservedPool的空间等同于一个query在一个机器上运行使用的最大空间大小,默认是10%的空间。
  • General则享有除了System Pool和General Pool之外的其他内存空间。
内存池使用原理
  • System Pool自然不必说,名义上是规定系统所用内存,实际是为了划定出query能够使用的内存上限。避免query使用内存太多,造成oom。
  • Reserved Pool, 那么当query非常多,并且把内存空间几乎快要占完的时候,某一个内存消耗比较大的query开始运行。但是这时候已经没有内存空间可供这个query运行了,这个query一直处于挂起状态,等待可用的内存。 但是其他的小内存query跑完后,又有新的小内存query加进来。由于小内存query占用内存小,很容易找到可用内存。这种情况下,大内存query就一直挂起直到饿死。
  • 所以为了防止出现这种饿死的情况,必须预留出来一块空间,共大内存query运行。 预留的空间大小等于query允许使用的最大内存。Presto每秒钟,挑出来一个内存占用最大的query,允许它使用reserved pool,避免一直没有可用内存供该query运行。
内存管理

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mQCfwc4o-1633421919319)(5C9F1782EBB94DD3A74D38138D546584)]

Presto内存管理,分两部分:

  • query内存管理
    • query划分成很多task, 每个task会有一个线程循环获取task的状态,包括task所用内存。汇总成query所用内存。如果query的汇总内存超过一定大小,则强制终止该query。
  • 机器内存管理
  • coordinator有一个线程,定时的轮训每台机器,查看当前的机器内存状态。当query内存和机器内存汇总之后,coordinator会挑选出一个内存使用最大的query,分配给Reserved Pool。
  • 内存管理是由coordinator来管理的, coordinator每秒钟做一次判断,指定某个query在所有的机器上都能使用reserved 内存。那么问题来了,如果某台机器上,,没有运行该query,那岂不是该机器预留的内存浪费了?为什么不在单台机器上挑出来一个最大的task执行。原因还是死锁,假如query,在其他机器上享有reserved内存,很快执行结束。但是在某一台机器上不是最大的task,一直得不到运行,导致该query无法结束。

容错

  1. 如果某个worker挂了,discovery service 会通知coordinator
  2. 对于query是没有容错的,一旦worker挂了,query就执行失败了,与其在这里容错不如直接执行
  3. coordinator 和discovery service 的单点故障问题还没有解决
  • 0
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值