Presto在字节跳动的内部实践与优化

在字节跳动内部,Presto 主要支撑了Ad-hoc查询、BI可视化分析、近实时查询分析等场景,日查询量接近100万条。本文是字节跳动数据平台Presto团队-软件工程师常鹏飞在PrestoCon 2021大会上的分享整理。

image.png 图注:PrestoCon 2021

在字节跳动内部,Presto主要支撑了Ad-hoc查询、BI可视化分析、近实时查询分析等场景,日查询量接近100万条。

  • 功能性方面

完全兼容SparkSQL语法,可以实现用户从SparkSQL到Presto的无感迁移;

  • 性能方面

实现Join Reorder,Runtime Filter等优化,在TPCDS1T数据集上性能相对社区版本提升80.5%;

  • 稳定性方面

首先,实现了多Coordinator架构,解决了Presto集群单Coordinator没有容灾能力的问题,将容灾恢复时间控制在3s以内。其次实现了基于histogram的静态规则和基于运行时状态的动态规则,可以有效进行集群的路由和限流;

  • 可运维性方面

实现了History Server功能,可以支持实时追踪单个Query的执行情况,总体观察集群的运行状况。

字节跳动OLAP数据引擎平台Presto部署使用情况

过去几年,字节跳动的OLAP数据引擎经历了百花齐放到逐渐收敛,再到领域细分精细化运营优化的过程。存储方面离线数据主要存储在HDFS,业务数据以及线上日志类数据存储在MQ和 Kafka。计算引擎根据业务类型不同,Presto支撑了Ad-hoc查询、部分BI报表类查询,SparkSQL负责超大体量复杂分析及离线 ETL、Flink 负责流式数据清洗与导入。

image.png

为了处理日益增长的Ad-hoc查询需求,在2020年,字节跳动数据平台引入Presto来支持该类场景。目前,整个Presto 集群规模在几万 core,支撑了每天约100万次的查询请求,覆盖了绝大部分的Ad-hoc查询场景以及部分BI查询分析场景。

image.png 图注:字节跳动内部 Presto 集群部署架构图

上图是我们 Presto 集群部署的架构,针对不同的业务需求拆分为了多个相互隔离的集群,每个集群部署多个 Coordinator,负责调度对应集群的 Worker;接入层提供了统一的 Gateway,负责用户请求的路由与限流;同时提供了 History Server,Monitor System 等附属组件来增加集群的可运维性与稳定性。

Presto 集群稳定性和性能提升

image.png

针对不同的业务场景以及查询性能要求,我们将计算资源拆分为了相互独立的 Presto 集群。Gateway 负责处理用户请求的路由,这部分功能主要通过静态的路由规则来实现,路由规则主要包括允许用户提交的集群以及降级容灾的集群等。为了更好的平衡不同集群之间的负载情况,充分有效的利用计算资源,我们后期又引入了动态的路由分流策略,该策略在做路由选择的过程中会调用各个集群 Coordinator 的 Restful API 获取各个集群的负载情况,选择最优的集群进行路由调度。通过静态规则与动态策略相结合的方式,Gateway 在为用户提供统一接入接口的情况下,也保证了集群之间工作负载的平衡。

image.png

Coordinator 节点是单个 Presto 集群的核心节点,负责整个集群查询的接入与分发,因此它的稳定性直接影响到整个集群的稳定性,在最初的部署中,我们每个 Presto 集群只能部署一个 Coordinator,当该节点崩溃的时候,整个集群大概有几分钟的不可用时间等待该节点的自动拉起。为了解决这个问题,我们开发了多 Coordinator 的功能,该功能支持在同一个 Presto 集群中部署多个 Coordinator 节点,这些节点相互之间处于 active-active 备份的状态。主要实现思路是将 Coordinator 和 Worker 的服务发现使用 Zookeeper 来进行了改造,Worker 会从 Zookeeper 获取到现存的 Coordinator 并随机选取一个进行心跳上报,同时每个 Coordinator 也可以从 Zookeeper 感知到其他 Coordinator 的存在。每个 Coordinator 负责存储当前连接到的 Worker 的任务负载情况以及由它调度的查询执行情况,同时以 Restful API的形式将这些信息暴露出去;其他 Coordinator 在做任务调度的时候会通过这些 Restful API 获取到整个集群的资源使用情况进行相应的任务调度。目前多 Coordinator 机制已经在我们的集群中上线使用了半年,将集群的不可用时间由几分钟降低为了3s以内。

image.png

另一个影响我们 Presto 集群稳定性的重要因素是超大规模的查询,在 ad-hoc 场景下,这种查询是无法避免的,并且由于这种查询会扫描非常多的数据或者生成巨大的中间状态,从而长期占用集群的计算资源导致整个集群的性能下降。为了解决这个问题我们首先引入了基于规则以及代价的查询时间预测,基于规则的查询时间预测主要会统计查询涉及到的输入数据量以及查询的复杂程度来进行预测,基于代价的查询时间预测主要是通过收集在 Catalog 中的 Histogram 数据来对查询的代价进行预测。上述预测能够解决部分问题,但是还是会存在一些预估不准的情况,为了进一步处理这些情况,我们引入了 Adaptive Cancel 功能,该功能主要是在查询开始执行后,周期性的统计查询预计读取的数据量以及已经完成的任务执行的时间来预测查询整体的执行时间,对于预测超过阈值的查询提早进行取消从而避免了计算资源的浪费,提升了集群的稳定性。

image.png

Presto 本身提供的 UI 界面可以很好的对查询的执行情况进行分析,但是由于这部分信息是存储在 Coordinator 内存当中,因此会随着查询数量的累积逐步清除从而导致历史的查询情况无法获取。为了解决这个问题,我们开发了 History Server 的功能,Coordinator 在查询执行完成之后会将查询的执行情况存储到一个持久化存储当中,History Server 会从持久化存储当中加载历史的查询执行情况并提供与 Presto UI 完全相同的分析体验,同时基于这部分持久化的信息,我们也可以建立相应的监控看板来观测集群的服务情况。

在不同场景的优化与实践

Ad-hoc查询分析场景

2020年之前,大数据场景下的ad-hoc查询主要由Hive/SparkSQL来支撑,为了进一步优化查询性能,提高资源使用效率,我们从2020年开始引入Presto。与SparkSQL相比,Presto是一个常驻的MPP架构的SQL查询引擎,避免了Spark Context启动以及资源申请的开销,端到端延迟较低;与Hive/Spark Thrift Server相比,Presto Coordinator更加成熟,轻量,稳定,同时Presto基于全内存的Shuffle模型可以有效的降低查询延迟。为了做到用户查询无感迁移到Presto,我们做了大量的工作使得Presto在语法和语义层面兼容SparkSQL。在接入层我们提供了SQL标准化改写功能,该功能可以将用户的SQL改写成Presto可以支持的SQL语法进行执行,做到了底层引擎对用户透明。在函数支持方面,我们在Presto中支持了Hive UDF的执行,使得之前数据分析师积累下来的大量UDF可以在Presto中执行,该功能主要支持了在解析阶段可以加载Hive UDF和UDAF,并进行类型转换使其适配Presto类型体系,最终封装成Presto内置函数的形式进行执行。该功能部分已经贡献回了Presto社区(https://github.com/prestodb/presto/pull/16737)。

image.png

BI可视化分析场景

Presto在字节跳动应用的另一个比较重要的场景是BI可视化分析。BI可视化分析提供了可视化交互的功能来进行数据分析,数据分析可以直观快速的进行数据分析并生成相应的分析图表,这给查询引擎提出了更高的要求。在这种场景下,QPS大大提高的同时,要求查询引擎能给出比较低的查询延迟。

为了应对这些挑战,我们做的一个比较重要的工作是在Presto中引入了物化视图。这种场景下,查询SQL往往都是由BI可视化平台根据固定的模版自动生成的,用户的可视化操作往往限于对查询过滤条件,聚合维度以及聚合指标的改变,适合物化视图的应用。

image.png

物化视图功能我们借鉴了很多传统数据库的经验,工作主要涉及三方面的工作:物化视图的自动挖掘主要根据用户查询的历史记录进行分析,统计不同数据的查询频率进行物化视图的自动推荐与创建;物化视图的生命周期管理主要维护分区级别物化视图的自动更新,删除;基于物化视图的重写功能基于已有的物化视图,对用户的query进行重写以减少查询执行的复杂度。

image.png

近实时场景的查询分析

这个场景是我们今年开始探索的一个场景,主要是为了降低数据链路的延迟,提升查询分析的时效性。传统的基于ETL的数据链路中,业务数据和日志数据经由Kafka定期dump到HDFS,然后会有多个ETL任务对数据进行加工清理形成不同层级的Hive表用来进行查询分析。这个链路中往往需要进行表数据的全量更新,任务比较重,与线上数据存在1天以上的数据延迟。为了降低数据延迟,我们引入了Hudi来进行数据的增量更新,在这个链路中,业务数据和日志数据经由Spark/Flink Streaming任务增量写入到Hudi表中,数据分析师可以直接查询这部分数据,这个链路目前可以做到分钟级别的数据延迟。我们在Presto这边做的主要工作是将Hudi表读取的功能从Hive Connector中提取出来成为了一个单独的Hudi Connector。Hudi Connector针对Hudi表的结构特点更好的支持了基于不同策略的分片调度算法,保证任务分配的合理性;同时Hudi Connector优化了Hudi MOR表读取过程中的内存管理,避免了Worker节点OOM,提升了集群稳定性;最后Hudi Connector的引入降低了Hudi版本升级带来的工作量,可以更好的集成Hudi社区最新的功能。这部分功能我们将会逐步贡献回社区(https://github.com/prestodb/presto/issues/17006)。

image.png

扩展阅读

本文中介绍的字节跳动内部Presto功能优化,目前已通过火山引擎数据产品“湖仓一体分析服务”向外部企业输出。

湖仓一体分析服务 LAS(Lakehouse Analytics Service)

面向湖仓一体架构的Serverless数据处理分析服务,提供一站式的海量数据存储计算和交互分析能力,完全兼容 Spark、Presto、Flink 生态,帮助企业轻松完成数据价值洞察。

更多关联产品:

大数据研发治理套件DataLeap

一站式数据中台套件,帮助用户快速完成数据集成、开发、运维、治理、资产、安全等全套数据中台建设,帮助数据团队有效的降低工作成本和数据维护成本、挖掘数据价值、为企业决策提供数据支撑。

火山引擎E-MapReduce

支持构建开源 Hadoop 生态的企业级大数据分析系统,完全兼容开源,提供 Hadoop、Spark、Hive、Flink 集成和管理,帮助用户轻松完成企业大数据平台的构建,降低运维门槛,快速形成大数据分析能力。

272
1
1
0
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论