Presto基本架构和原理

Posted by SH on November 20, 2020

Presto

简介

Presto是一种用于大数据的高性能分布式SQL查询引擎。 其架构允许用户查询各种数据源,如Hadoop、AWS S3、Alluxio、MySQL、Cassandra、Kafka和MongoDB。 甚至可以在单个查询中查询来自多个数据源的数据。Presto是Apache许可证下发布的社区驱动的开源软件。

Presto是由Facebook开发的一个分布式SQL查询引擎, 它被设计为用来专门进行高速、实时的数据分析。 它的产生是为了解决Hive的MapReduce模型太慢以及不能通过BI或Dashboards直接展现HDFS数据等问题。 Presto是一个纯粹的计算引擎,它不存储数据,其通过Connector获取第三方Storage服务的数据。

历史:

  • 2012年秋季,Facebook启动Presto项目
  • 2013年冬季,Presto开源
  • 2017年11月,11888 commits,203 releases,198 contributors

功能和优点:

  • Ad-hoc,期望查询时间秒级或几分钟;
  • 比Hive快10倍;
  • 支持多数据源,如Hive、Kafka、MySQL、MonogoDB、Redis、JMX等,也可自己实现Connector;
  • Client Protocol: HTTP+JSON, support various languages(Python, Ruby, PHP, Node.js Java);
  • 支持JDBC/ODBC连接;
  • ANSI SQL,支持窗口函数,join,聚合,复杂查询等;

应用场景:

Presto应用场景中,主要还是查询时延要求在毫秒或者秒级别的SQL并发BI报表,Ad-Hoc查询以及多数据源关联查询居多, 用Presto来做ETL工具的不多,毕竟Spark,Flink这些成熟的流批计算工具,用作ETL工具相对更成熟一些。

prestodb和prestosql:

PrestoDB是2013年Facebook的三个核心工程师创造和开源出来的,在Facebook内部,它的应用规模是很庞大的(部署了多个集群,总节点数10000+台), 这三个工程师一直想把Presto发扬光大,但是一直到了2019年,他们感觉到公司好像不怎么给力, 同时期的三个开源大数据技术Spark、Flink、Kafka都已经创建了自己的商业化公司推广到家喻户晓了, 如果说哪家公司在力推Presto,可能只有一家叫Teradata的小公司。

无奈之下,这三位核心工程师离职加入了刚成立两年的Starburst公司,这家公司Fork了Presto的项目源码,取名为PrestoSQL, 创建了自己的代码仓库和官方网站,在做商业化运营的Presto。

如果你问笔者该选哪个,笔者更倾向于选择PrestoSQL,因为他近两年的源码迭代速度更快,而且还有三位创始人的支持,相信PrestoSQL的发展前景。 不过事情也不是绝对的,PrestoDB与PrestoSQL也在互相学习,并且会把对方比较好的实现,merge到自己的项目里,所以同时关注一下这两个项目的动态,没有坏处。

架构与原理

基本架构

  • Master-Slave架构
  • 三个模块:
    • Coordinator、Discovery Service、Worker
  • Connector

Presto架构

Presto沿用了通用的Master-Slave架构,Coordinator即Presto的Master,Worker即其Slave, Discovery Service就是用来保存Worker结点信息的,通过HTTP协议通信,而Connector用于获取第三方存储的Metadata及原始数据等。

  • Coordinator负责解析SQL语句,生成执行计划,分发执行任务给Worker节点执行;
  • Worker节点负责实际执行查询任务;
  • Worker节点启动后向Discovery Server服务注册,Coordinator从Discovery Server获得可以正常工作的Worker节点;
  • 假如配置了Hive Connector,需要配置一个Hive MetaStore服务为Presto提供Hive元信息,Worker节点与HDFS交互读取数据。

Presto的架构实际上就是一套分布式的SQL执行架构,它最大的特点就是天然就是存储计算分离,Presto只负责计算,存储的部分由数据源自身提供。 这种存储与计算分离的架构很符合当今云计算发展的趋势——独立的存储云服务与计算云服务,这样做的好处是存储资源不够时可以独立扩容存储,计算资源不够时可以独立扩容计算, 而且计算和存储能够分别使用适合自己的机型。

分布式的存储系统的实现一般都比较复杂,涉及到数据的分区、副本、容灾、文件格式、IO优化等,是一项非常大的工程。 Presto直接放弃了存储,只做计算,而且它的计算也是做了一些妥协的,如不支持单个Query内部的执行容错,如果Query中的某个Task失败了,会导致整个Query失败。 这样实现起来更简单,代价是需要用户侧来重试。

查询过程

整体查询流程为:

  • Client使用HTTP协议发送一个query请求;
  • 通过Discovery Server发现可用的Server;
  • Coordinator构建查询计划(Connector插件提供Metadata);
  • Coordinator向workers发送任务;
  • Worker通过Connector插件读取数据;
  • Worker在内存里执行任务(Worker是纯内存型计算引擎);
  • Worker将数据返回给Coordinator,之后再Response Client。

Presto-查询流程

既然Presto是一个交互式的查询引擎,我们最关心的就是Presto实现低延时查询的原理,我认为主要是下面几个关键点,当然还有一些传统的SQL优化原理,这里不介绍了。

  • 完全基于内存的并行计算;
  • 流水线;
  • 本地化计算;
  • 动态编译执行计划;
  • 小心使用内存和数据结构;
  • 类BlinkDB的近似查询;
  • GC控制;

SQL执行流程:

Presto-SQL执行流程

当Coordinator收到一个Query,其SQL执行流程如上图所示。

  • SQL通过Anltr3解析为AST(抽象语法树),然后通过Connector获取原始数据的Metadata信息,这里会有一些优化,比如缓存Metadata信息等;
  • 根据Metadata信息生成逻辑计划,然后会依次生成分发计划和执行计划;
  • 在执行计划里需要去Discovery里获取可用的node列表,然后根据一定的策略,将这些计划分发到指定的Worker机器上,Worker机器再分别执行。

数据模型

Presto 使用 Catalog、Schema和Table 这3层结构来管理数据。如图:

Presto-数据模型

  • catalog:就是数据源。每个数据源连接都有一个名字,一个Catalog可以包含多个Schema;
  • schema:相当于一个数据库实例,一个Schema包含多张数据表;
  • table:数据表,与RDBMS上的数据库表意义相同。

在Presto中定位一张表,一般是catalog为根,例如:一张表的全称为 hive.test_data.test,标识hive(catalog)下的 test_data(schema)库中 test 表。

可以简理解为:数据源的类别.数据库.数据表。

可使用:show catalogs查看数据源;show schemas from hive查看数据库实例;show tables from default查看表。

切换当前使用的实例(在同一个数据源内切换无需指定catalog 前缀):use hive.default

Connector

Presto设计了一个简单的数据存储抽象层,来满足在不同数据存储系统之上都可使用sql进行查询。 存储插件(连接器connector)只需提供实现以下操作的接口,包括对元数据的获取,获得数据存储的位置,获取数据本身的操作。

  • ConnectorMetadata: 管理表的元数据,表的元数据,partition等信息。在处理请求时,需要获取元信息,以便确认读取的数据的位置。Presto会传入filter条件,以便减少读取的数据的范围。元信息可以从磁盘上读取,也可以缓存在内存中。
  • ConnectorSplit: 一个IO Task处理的数据的集合,是调度的单元。一个split可以对应一个partition,或多个partition。
  • SplitManager : 根据表的meta,构造split。
  • SlsPageSource : 根据split的信息以及要读取的列信息,从磁盘上读取0个或多个page,供计算引擎计算。

插件能够帮助开发者添加这些功能:

  • 对接自己的存储系统;
  • 添加自定义数据类型;
  • 添加自定义处理函数;
  • 自定义权限控制;
  • 自定义资源控制;
  • 添加query事件处理逻辑。

References