前言
团队需要一个OLAP引擎,在对比了多种开源系统后,选择了Presto。简单总结下,以后可能会写一些源码解读,二次开发,问题与调优的文章。
我们的需求是一个支持多种组件的统一SQL引擎,以及资源队列,权限控制,SQL监控管理等上层功能,性能当然越快越好。
Hive
太古老了,Hive3倒是添加了很多如LLAP,TEZ引擎优化,更成熟的事务特性等,但是不方便支持第三方组件如ES,设计的StorageHandler扩展接口很蠢很死板,用它的MetaStore就可以了。。好像反正不管hive怎么优化,都是被别人随便10x性能PK的倒霉蛋。
Impala
这个重点是跑HDFS的Hive表(还有Hbase/Kudu)像其他组件支持的不多,并且元数据设计太重了,以前动则缓存整个表分区文件block等信息,还广播到集群。不过最新4.x版本好像在改进这方面问题 也太慢了。。
Spark
作为批处理的业界老大,用了几年SparkSQL,并不适合作为数据仓库产品级的东西,没有细粒度的权限控制(与Sentry/Ranger都不好集成 官方都不做),只做了检索相关的SQL,没有扩展性的权限/角色等定义,也没有资源隔离,单点调度(当然正常情况下Presto也是,但是presto是流水线式调度的,task是迭代器模式,而Spark是一下子产生全部静态集合的Task,即使limit 10,这样元数据压力极大)等等 很多地方 不适合作为一个大集群的对外JDBC出口。提供了DataSource扩展接口,第三方组件组要靠个人或者其他团队支持(不是官方库提供,总觉得质量无法保证)。因为本质上Spark作为批处理框架,不是作为数据库去设计的。。虽然SparkSQL社区投入了巨大的力量去做DataSet统一优化。。可能某个单一语句SparkSQL性能胜出,但是作为整体功能性评估是不够的。
像其他Druid,ClickHouse,Kylin,Greenplum要么是单一工具,要么扩展性不好,就不考虑了。。
Presto简介
- 基于内存的并⾏计算,分布式SQL交互式查询引擎(当然也支持Spill到Disk)
- Massively parallel processing (MPP)架构
- 多个节点管道式执⾏,性能不输SPARK,比hive快10倍+
- 由Coordinator和多个Worker组成
- 支持大量的组件,提供统一SQL入口,只需要配置即可【重点】
- 有丰富的数据库特性:队列、优先级、权限、资源隔离、WEBUI、等
[官方团队有个类似最佳实践/cook-book的文档] (https://trino.io/blog/2020/04/11/the-definitive-guide.html)
PS:当前版本 0.258支持的Connector有
Accumulo Connector // kv 类似hbase
BigQuery Connector
Black Hole Connector // 类似 /dev/null
Cassandra Connector
Druid Connector
Elasticsearch Connector // 重点使用
Hive Connector // 重点使用
Hive Security Configuration
Iceberg Connector
JMX Connector //好像也只是本集群的信息 。。
Kafka Connector
Kudu Connector
Local File Connector // 好像可以作为本地数据库 ?
Memory Connector // 并不能持久化 。。
MongoDB Connector
MySQL Connector
Oracle Connector
Apache Pinot Connector
PostgreSQL Connector
Redis Connector
Redshift Connector
SQL Server Connector
System Connector // 本集群的信息
Thrift Connector
TPCDS Connector //快速构建数据集和测试
TPCH Connector
关于Presto与Trino
Trino是由原团队由于与FaceBook管理层理念不一致,而开的新分支,社区分裂的细节不清楚,反正现在两边都在更新,社区也都很活跃,过了2年,具体的代码区别已经非常大了,合并几乎是不可能的事情。。
选Trino还是Presto纠结了许久,Trino毕竟是创始团队支持的,并且好像现在支持的Connector数量更多,我个人两边代码都下载浏览了一下,Trino的代码组织和结构好看的多,性能对比没有实测,但是看GitHub上两边的RoadMap与Blog来看,似乎Presto研发力量更强一些。。(只是个人感觉 欢迎讨论)
PS:以Coordinator的HA为例
Trino社区在2019就提出了。。现在依然没有release
而Presto已经实现了,并且还有RaptorX这个分层缓存(在存算分离架构下有很好的性能提升)等,另外Presto这边还计划做Coordinator的负载均衡,和HA还不是一回事,这在大集群里需求挺强烈的。
[分层缓存介绍] (https://prestodb.io/blog/2021/02/04/raptorx)
来个presto的执行图(这类东西大同小异,与SparkSQL差不多也能对应)
Presto主要概念
Coordinator
对外负责管理集群与客户端的连接,并接收客户端查询请求。
进行SQL的语法解析、查询计划生成和优化,并进行查询任务的调度。
集群的管理节点。内置了discovery server,跟踪Worker节点的状态
部署情况:一般作为单独节点部署在集群中
通信方式:使用RESTful接口与客户端、Workers进行交互
Worker
集群的工作节点。用于执行被分解后的查询任务(task)及处理数据
部署情况:一般集群中部署多个worker节点
通信方式:使用RESTful接口与Coordinator、其他Workers进行交互
数据源 Connector
Presto通过connector可以访问多种不同的数据源。
connector相当于数据库访问的驱动。
每种connector通过实现Presto的SPI接口实现数据源的标准接入。
Catalog
Catalog可以包含多个schema,并且通过使用指定的connector访问指定的数据源。例如,通过配置Hive catalog来访问Hive数据源。
Schema
作用:用于管理表,类似于Mysql中的database。一个catalog及一个schema可以唯一确定一组可查询的表集合。
Table
与传统关系数据库的概念类似。从数据源到表达映射由connector指定。
元数据结构图
Presto查询执行模型
Statement
Presto支持ANSI标准的SQL语句,这些语句包括子句(caluses)、表达式(expressions)、断言(predicates)组成。
Presto为什么要区分statement和query点概念呢?
在Presto中,statesments指的是用户输入的SQL语句的文本表示。
当statement被执行时,Presto会创建查询执行及查询计划用于执行相应query,
而查询计划是在一系列Worker节点上分布式执行的。
【This is necessary because, in Presto,
statements simply refer to the textual representation of a SQL statement.
When a statement is executed,
Presto creates a query along with a query plan that is then distributed across a series of Presto workers.】
Query
当Presto接收到一个SQL语句(statement),
会将其转换为一个查询执行(Query),同时创建查询计划(query plan)。
其中,查询计划是运行在Prestor Workers上的一系列相关联的stages。
Statement 与 Query的差别:Statement是输入给Presto的SQL文本;
Query是用于为了执行Statement而实例化出来的一些列配置及组件。
一个Query包含stages、tasks、splits、connectors等组件和相应的数据源这些概念。
Stage
当Presto执行Query时,会将query拆分成具有层次关系的多个stages。
例如,Presto从Hive中查询1亿条记录并进行数据聚合时,
Presto会创建一系列的stages用于执行相应的分布式查询,
同时创建一个root stage用于聚合上述stages的查询输出,
之后将结果聚合后输出给Coordinator,并进一步输出给用户。
一个Query的stages之间是树形的层次结构。
每一个Query都有一个Root stage,用于聚合所有其他Stages的输出数据。
Stage只是coordinator用于分布式查询计划(query plan)建模的逻辑概念,
本身并不会执行在Presto Workers上。
Task
Presto是通过Task来运行的。
一个分布式查询计划(query plan)被拆解成一些列的stage。
一个stage分解成一系列并行执行的task。
每个task被分解成一个或多个并行的driver,每个driver作用于一系列splist上。
每个task都有对应的输入输出。
Driver
一个Task包含一个或多个driver。
Drivers处理数据,并由task聚合后传给下游stage的一个task。
一个Driver就是作用于一个split上的一系列operator的集合。
Driver是Presto架构最底层的并行处理单元。
每个driver都有一个输入和一个输出。
Operator
一个Operator代码对于一个Split的一种操作。
一个Operator依次读取一个Split中的数据,
将Operator所代表的计算和操作应用于该split上,并产生输出。
如读取表文件的Operatior。
Split
一个split是整个大的数据集上的一个小的切片。
位于分布式query plan中较低层次的stages从数据源获取splits,
位于较高层次的中间stages则从其他stages取数据。
Exchange
Exchange用于不同Presto节点间的数据交换。
Task生产数据放入输出缓存中,
也可以通过exchange客户端从其他task消费数据。