SparkCruise: 托管群集中的工作负载优化

699 阅读15分钟

论文导读 SparkCruise: Workload Optimization in Managed Spark Clusters at Microsoft, 转载请标明出处。

1. Introduction

云计算公司提供全面管理的Spark服务,用户数量和任务数一直在激增。然而,云提供商和用户都缺乏优化这些大规模工作负载的工具和时间。为了解决这个问题,微软设计了SparkCruise,在Spark优化器中添加基于负载的反馈循环来帮助用户分析和优化任务的工作负载。 Spark发展历程:

  • 2010年提出了分布式数据集(RDD),通过重用计算数据提高多个并行操作的性能
  • 2013年Shark使用Spark作为处理后端,运行声明式的SQL查询
  • 2015年Shark演变为Spark SQL,提供Catalyst查询优化器,包含Rule-Base&Cost-Based 优化规则
  • 2020年Spark引入AE,自适应查询执行

但是在生产环境中,即使给定了部署环境和数据集,仍然很难在系统本身预先构建正确的优化集,这使得基于工作负载进行实例优化变成了一种新的思潮。事实上,Spark提供了数百种配置,可以根据不同的工作负载进行调整。然而,手动调整配置使系统适应给定的负载变得异常困难。幸运的是,现代的云部署提供了大量的工作负载数据集,可以帮助我们构建一个工作负载驱动的反馈循环,动态的调整系统运行。SparkCruise的主要贡献有:

  • 构建SparkCruise平台,在Spark中添加工作负载驱动的反馈循环,并讨论它如何将Spark引擎从一次优化一个查询转换为优化端到端工作负载
  • 构建了Spark查询计划的信息收集管道,在生产环境中以极低开销收集匿名的spark sql查询计划
  • 引入了一种工作负载的表示方法,它结合了工作负载的编译时和运行时的特征
  • 介绍了微软spark生产环境的工作负载的详细情况,包括输入、作业、查询、运算符、基数、过滤程度和查询计划模式(如宽度和高度)的分布。
  • 描述了为HDInsight的客户构建和发布的工作负载查询平台,帮助客户分析任务的运行情况
  • 深入研究了计算重用,将其作为SparkCruise的具体优化项目,并讨论了视图选择和物化视图的重用机制和各种在线和离线策略

2. Spark background(Microsoft)

主要有两类用户:

  • 大量的周期性ETL任务
  • 一些ad-hoc/notebook任务(逐渐增加)

SparkCruise只优化Spark SQL查询,忽略直接使用RDD编程的任务。原因是,只有声明式的dataframe才会通过Catalyst查询优化。

所以整个 SQL 的执行过程可以使用下图表示(论文没有提及,帮助读者理解): image.png

3. SparkCruise Overview

SparkCruise由四部分组成

Query plan telemetry(查询计划信息收集器):通过配置项添加日志监听器Log Listener,收集spark sql的查询计划。查询计划为JSON格式,隐藏了用户的个人信息,查询计划的每一个节点都打上了唯一标签。

Workload representation(工作负载表示):通过查询计划信息收集器的收集整理,工作负载被抽象为一种表示方法,用来优化后续的查询。整个过程包括构建Workload table、逻辑计划和物理计划的节点关联、丢失或者不合规信息的清理。

Workload insights notebook(工作负载分析平台):本质是查询workload table,分析集群或者单个任务的模式、大小、性能、代价等,并为用户提供了预制的查询模板

Workload optimization(工作负载优化):将workload table作为原料,可以为各种优化算法提供方便易用的数据集(例如,从过于的工作负载中学习的基数模型)。SparkCruise把优化结果写进一个feedback反馈文件中,并通过Catalyst加载这些优化信息,以此达到系统的自我调试。

4. Query plan telemetry(查询计划信息收集器)

SparkCruise的关键是收集Spark sql查询计划的元信息(叫统计信息、metrcis信息也可以),论文提出五个重点:

  • 不改动源代码
  • 低开销,不影响查询性能
  • 用签名标识查询计划,便于模式匹配和学习
  • 擦除隐私信息
  • 支持多种场景

Telemetry包含以下四个步骤:

4.1 Plan listener

Spark原生就包含application/job/stage/task/query plan的元信息监听器,用户可以实现SparkFirehoseListener接口监听和收集这些元信息。只是query plan的信息和细节是不完善的,尤其是query plan的信息是text格式,有些内容由于太长被截断了,幸运的是Catalyst包含JSON序列化SparkPlan的方法。query plan包含四个阶段的信息:parsed plan, analyzed plan, optimized plan, physical execution plan。经过测试,监听器带来的开销非常的低,在微软的线上有那种异常大的query plan,这时候不得不做一些截断处理。

4.2 Plan Annotations

SparkCruise通过历史的工作负载来优化未来的查询,所以需要对query plan做标记。query plan是由Node组成的树形结构,每一个节点Node都有一个标签。标签是通过hash函数计算出来,hash函数包含了node-level和query plan strcuture。提供两种默认的标签:

Strict Signatures:包含node-level已经它的子节点的完整信息,如果为叶子节点,还包含了数据值和数据版本

Recurring Signatures:只包含node-level已经它的子节点的信息,如果为叶子节点,不包含数据值和数据版本。

4.3 Plan anonymization

SparkCruise混淆了filenames, table names, column names等用户信息,并在parsed, analyzed, optimized, physical plans 中使用相同的混淆编号。此外,query plan中会保留column的id编号,这是从叶子节点自增的,不带有隐私信息。

4.4 Telemetry pipeline

打上标签和脱敏的query plan通过SparkFirehoseListener发射到Figture 1的终端上,不同终端收集器有不同的用途。

Azure Blob Filesystem:写入对象存储中,供用户分析处理

Azure Data Explorer:在线数据集,可以直接交互式分析

Cosmos:微软离线数据库中,压缩存储,离线分析

5. Workload representation(工作负载表示)

Query plan telemetry收集的数据准确来说是时序数据,workload representation需要把这些raw event转换成可以共享的公共负载库(可以供查询分析的数据集)。

5.1 Plan linking(计划关联)

查询执行结束后,SparkCruise收集不同阶段的计划信息:parsed plan, analyzed plan, optimized plan, physical plan, executed plan,列表中的后一个计划由前一个计划生成,但是spark没有保留不同阶段plan的node关联信息。这导致logical plan无法获得operator的运行时基数和成本。具体来说,SparkCruise通过Logical plan中计算得来的strict signature来关联可以应用的物化视图和计算重用(无法重用的,也可以通过recurring signatures估计operator的代价),cost-based的视图选择也需要考虑逻辑运算符的成本。因此,需要将不同阶段plan的node关联起来。 image.png 论文提出了两种关联算法:

Top-down heuristic based: 使用提前定义的启发式规则,从根节点开始查找和匹配。常见的规则:

  • 跳过physical plan的exchange
  • Logical plan试着关联physical plan的Exchange, Sort, SortMergeJoin
  • 文字相似度匹配,例如logical plan的Aggregate对应physical plan的HashAggregate

Bottom-up similarity based: 使用另外一篇论文的Cupid算法,从叶子节点开始向上匹配,匹配最大子树,也采用文字相似度作为匹配依据。

论文发现top-down的方式更好,如果可以侵入spark源码,甚至可以直接修改Optimizer来保留node的关联信息。

最后的步骤是physical plan关联executed plan(也可以侵入Planner直接关联),这个过程和logical plan关联physical plan类似,就不再赘述。

通过以上步骤的关联操作,optimized logical plan可以通过标签在workload represenstation表中找到该操作实际执行的物理代价,这个代价值可以直接作为cost-based优化器的计算值(输入)。

  • 论文没有详细描述该过程,读者可参考砖厂的Cost Based Optimizer in Apache Spark。简单来说就是cost-based本身需要input信息、histogram分布信息,然后通过一些估计算法大概计算operator的代价。但是通过统计信息估计得来的代价值是不准确,本身也耗时,SparkCruise降维打击,直接告诉Optimizer各个operator的物理代价是什么,帮助Optimizer做最好的优化。如果某个subexpression有物化视图和计算重用,还可以直接拿来用。

5.2 Data cleaning(数据清洗)

查询结束后,Spark报告了运行时指标,包括:cardinality, size, time, and memory。但是这些指标基本都是stage粒度的,很少有针对单个operator的metrics信息。因此,即使经过Plan linking操作,也缺少部分Operator的代价信息。为了解决这个问题,论文提出两种插值策略:

  • Spark提供了scan, filter, aggregates, joins的基数信息,其它operator的信息可以通过后序遍历的方式计算得到。遍历中,如果某个operator缺少基数信息,就从子节点中复制,如果join操作缺少基数信息,就选择子节点中最大的。
  • Spark统计了每个stage的执行时间,将stage时间分配给串行的operator,使得串行的operator的时间加起来等于总执行时间。基于基数和时间,就可以应用cost-based选择最优的执行计划(* 论文并没有讲如何估计串行operator的时间,也没有讲并行operator的影响)

5.3 Workload table

Workload table是基于工作负载的优化算法的基础。workload table包含多维度信息:application metadata, query metadata, metrics, query plans, annotations。

如4.4小节所述,查询计划的元信息保存在多个位置(从对象存储到数据库)。workload table通过独立于telemetry的分析器(workload parser job)生成,workload parse job可以读取和写入不同的数据源。

Workload parser job构建包含层级信息的workload table,表中的一行表示一个operator的metrics信息,包含所有的细节信息:physical operator, application-level metadata, query-level metadata, linked logical operator details, and compile-time and run-time statistics。具体的构建算法,可以使用简单的先序遍历,遍历过程中传递父application/query/operator的信息。

6. Production insights

本节是第一次描述大规模生产环境中的Spark SQL负载情况。微软的HDInsight包含176 cluster, 114 subscriptions, 34834 applications, 349366 query, 1438411 sub-expressions。

6.1 Subscriptions, Tenants, Applications

拥有Azure订阅的HDInsight客户可以创建一个或多个租户(或群集)。每个租户可以运行多个应用程序,并且应用程序可以有多个Spark SQL查询。

Subscriptions: 用来关联Azure账户和Azure资源服务的东西

Tenants: 租户表示一个组织,多租户构架下享有一样的软件环境,租户与租户之间进行数据隔离

Figure 4展示:

  • 30%的订阅有多个租户
  • 30%的租户有100个以内的application,5%的租户有1000个以上的application
  • 大多数application的query少于10个,仅有5%的application含有10个以上的query

6.2 Inputs

Figure 5展示了input table的基数大小和过滤效果的CDF图

  • 仅有10% input table的行数超过了10M行, 30% input table的行数少于1K行
  • query维度下,60% query的输入超过了10K行
  • 80%的filter操作的过滤通过率超过了20%(多数为非NULL操作)

6.3 Operators

统计operator的分布情况,帮助定位性能的关键路径

  • 超过80%的query含有多个join
  • 少于20%的query含有多个projection, filters, aggregates

  • 90%的join为broadcast hash join,8%的join为sort merge join
  • 95%的shuffle小于1MB,非常非常少的shuffle大于100G(?100MB)

6.4 Queries

  • 80% query的operator数不超过5个
  • 90% query的深度和宽度不超过10

7. Insight notebook

SparkCruise提供了可以分析查询workload详情的notebook,本质其实就是一个jupyter notebook,论文还提供了预制的查询模板

首先需要准备feedback文件,Azure HDInsight官网提供了使用SparkCruise为Spark application生成feedback文件的方法。拿到feedback文件以后,就可以用jupyter进行交互分析。

例如可以进行operator频率统计。

SparkCruise对TPC-DS进行了workload分析,得到以下结果

TPC-DS的input比微软生产环境的大,filter过滤效果的分布和生成环境类似。

TPC-DS的Projection和join更多,sort merge join的数量提高三倍以上

TPC-DS 30%的shuffle大小超过100GB(?论文中是100G,看图是100MB)

TPC-DS query的operators更多,query的深度和宽度更大。

8. Automatic computation reuse

本节讲解具体的workload优化——在大量的Spark SQL计算任务中重用公共计算,这个方法已经在微软的另外一篇论文(SparkCruise: Handsfree Computation Reuse in Spark)中介绍(*四页,特别简单,看不出细节)了,本节是对该论文的回顾。关于物化视图和计算重用的过程,以及如何匹配子表达式的细节,可以参看:

Selecting Subexpressions to Materialize at Datacenter Scale

In-memory Caching for Multi-query Optimization of Data-intensive Scalable Computing Workloads

8.1 Workloads

SparkCruise支持两类查询使用计算重用

Recurring workloads(周期任务):这类任务每天执行相同的查询逻辑,包含不同的输入和参数,可以通过历史workload对这类任务进行优化。

Ah-hoc(分析任务): 这类任务其实也包含大量的重复算子,在TPC-DS workoad中可以看到大量的重复的aggregates, filters, joins, projections, sorts。事实上,超过一半的filter在查询中重复,而16%的aggregate和13%的join也在多个查询中重复。

8.2 View feedback

SparkCruise基于cost-based提供视图选择算法,以达到最优计算。workload table中包含strict和recurring签名,strict签名被用在视图选择算法中,以此来达到代价最低的视图物化和计算重用。这些被选择的视图会上传到可以公共访问的位置,当相同签名在周期任务和adhoc任务的Plan中出现时,就可以使用计算重用。

8.3 Reuse mechanics

SparkCruise添加了两个Optimizer rules —— 在线物化和计算重用。在线物化和计算重用都可以读取feedback信息,来改变执行计划。这两条Optimizer rules将传统的Spark查询优化器转换为工作负载感知优化器。如果子查询存在基于工作负载的反馈feedback中,但尚未物化,则在线物化子查询的结果。类似地,计算重用通过扫描物化视图来替换子查询,以避免其计算开销。图15显示了query 42对公共查询结果的自动物化,以及TPC-DS的query 52对物化视图的后续重用。

8.4 Case studies

物化视图包含三个阶段:

  • 视图选择
  • 视图物化
  • 视图匹配和重用

视图选择和视图物化一般是离线过程,视图匹配和重用是在线过程。

离线视图选择&离线视图物化:给视图的recurring签名添加计数功能,可以选择物化最频繁使用的视图。需要注意的是,周期任务虽然有相同的查询逻辑,但是输入和参数可能不同,这时候需要restrict签名来验证计算是否可以重用。所以,最好的方式是根据周期任务的执行时间,提前物化需要的视图数据。图14展示了TPC-DS query 42和52在提前物化视图的情况下,相比baseline有34%的性能提升。

离线视图选择&在线视图物化:相比较离线物化视图,SparkCruise支持在线物化视图,第一条命中物化规则的查询也许会增加运行时间,但是后续包含相同子查询的sql会缩短执行时间。图14显示query 42运行时间增加,query 52运行时间减少。在TPC-DS测试中,整体运行时间减少9.3%。

在线视图选择&在线视图物化:周期任务都可以通过离线的方式选择视图,但是目前adhoc类型的分析任务日益增加。通过统计分析,adhoc任务的前置查询,也会在后续重复查询。SparkCruise支持分析前置的sql查询,抽取出大概率还会重复使用的查询结果进行物化( *论文没有说细节,可能还是计数)。在TPC-DS数据集中,该模式有比较大的性能回退。论文选择了另外一个数据集IDEBench(交互式和分析查询场景更多)进行验证,发现该模式依然有9.6%的性能提升。

9. Conclusions

重申论文贡献:

  • 大规模生产环境中,收集查询计划的元信息的方法
  • workload的抽象表示方法,可以提供给各种优化算法
  • 基于负载的计算重用
  • 基于负载的查询优化

SparkCruise已经在微软生产环境中部署和应用,优化效果显著,后续将继续基于workload优化Spark性能。