(论文阅读-优化器)A Cost Model for SPARK SQL

目录

Abstract

1 Introduction

2 Related Work

3 Background and Spark Basics

4 Cost Model Basic Bricks

4.1 Cluster Abastraction and Cost Model Parameters

4.2 Read

4.3 Write

4.4 Shuffle Read

4.5 Broadcast

5 Modeling GPSJ Queries

5.1 Statistics and Selectivity Estimates

5.2 The SC() Task Type

5.3 The SB() Task Type

5.4 The SJ() Task Type

5.5 The BJ() Task Type

5.6 The GB() Task Type

5.7 Handling Stragglers


Abstract

本文我们为Spark SQL提出了一个新的cost model。这个cost model涵盖了广义的Projection、Selection、Join(GPSJ)查询。cost model考虑了网络和IO成本,以及最相关的CPU成本。执行成本是从Spark生成的物理执行计划开始计算的。Spark在执行GPSJ查询时采用的操作集合是基于集群和应用参数以及一组数据库统计数据来进行代价的分析建模的。在三个基准测试和两个不同规模和不同计算特征的集群上进行的实验结果表明,我们的模型可以估计实际执行时间,平均误差约为20%。这样的精度足以让系统在执行时间差有限的情况下选择最有效的方案。如果分析模型与我们的离散处理策略相结合,误差可以降低到14%。

1 Introduction

大数据改变了数据处理和分析的方式。需要处理的数据量及其多样性推动了一系列解决方案和平台的发展。在这种情况下,Apache Hadoop是在过去几年中比其他任何框架都更受欢迎的框架。Hadoop的第一个版本被严格限制在mapreduce编程范式中。MapReduce需要熟练的程序员,因此Hadoop设计师试图通过提供更高级和易于使用的编程环境来减轻用户的压力,例如Pig和hive——第一个基于Hadoop和MapReduce的SQL系统。

Spark是一个快速且通用的计算引擎,用于运行在Hadoop上的大规模数据处理。它通过支持transformation的流水线构建在MapReduce范例之上。通过这种方式,它减少了将数据写回磁盘的次数,并且可以比MapReduce[1]快几个数量级,同时保留了容错功能。Spark可以处理存储在多个存储引擎(如MongoDB、Cassandra)中的数据。在这项工作中,我们将重点关注Hadoop(和HDFS),这是迄今为止Spark最常用的架构。特别是,Spark包含了一个基于SQL的子系统:标准的SQL查询被根据Spark命令重写,并在集群上并行执行。Spark SQL的性能允许对大数据进行在线计算,许多OLAP供应商(如Tableau和Micro Strategy)已经提供了与他们系统的连接。

尽管Spark已经被广泛采用,但它仍在开发中,并在不断发展。Spark SQL引擎不能被认为像传统的关系dbms那样成熟,许多性能改进仍然是可能的。特别是,负责将SQL查询转换为Spark命令序列的模块Catalyst[2]仍然依赖于基于规则的优化器,并且关于预测运行SQL查询的成本的成本模型所做的工作很少。

在本文中,我们提出了一个Spark SQL的成本模型,该模型涵盖了在[3]中首次研究的GPSJ(广义投影/选择/连接)查询类。GPSJ查询由join、selection predicates和aggregation组成。由于这三个操作符不一定都必须出现,因此我们的成本模型也涵盖了简单的selection和join查询。尽管已经为通用MapReduce应用程序开发了其他成本模型,但据我们所知,这是第一个考虑到Spark计算范式和Spark SQL的结果。更详细地说,我们的成本模型有以下特点:

  • 它依赖于数量有限的任务类型,这些任务类型如果组合得当,可以对一系列SQL查询进行建模。由于这些任务类型的行为是已知的,它们的成本模型可以被分析塑造,从而提供准确的估计。

  • 它不需要旨在捕获通用作业行为的复杂作业配置文件(例如,[4]),而是基于一小部分集群和应用程序特性以及db统计数据来进行估计。

  • 成本函数根据Spark成本对SQL执行计划进行建模。这允许对执行成本进行sql感知的评估。我们相信将成本评估转移到更概念化的层次可以帮助sql用户更好地理解系统行为。

  • 返回的成本不是逻辑成本,而是考虑到集群特性的实际执行时间。这样就支持集群性能分析和集群调优。

虽然这个成本模型是专门为Spark构建的(在任务类型和系统行为方面),但它可以推广到其他大数据SQL引擎,如HIVE和Impala。我们的方法的新颖性超越了Spark SQL的界限;它代表了一种在大数据平台上计算执行SQL查询成本的原始方法。

执行时间是通过将执行由Catalyst生成的物理计划代码的树的节点所需的时间相加得到的。成本模型基于磁盘访问时间和跨集群节点传输数据所花费的网络时间。我们还考虑了数据序列化/反序列化和压缩的CPU时间。这些成本是由磁盘吞吐量隐式计算的。正如在[1]中所解释的,spark的工作负载主要是一次性的,如SQL查询产生的工作负载,要么是网络绑定的,要么是磁盘绑定的,而CPU可能成为序列化和压缩成本方面的瓶颈。对于通常受cpu限制的multi-pass2工作负载来说,情况并非如此。在不同规模和不同计算特征的集群上的实验结果表明,该模型可以估计实际执行时间,平均误差在20%左右。这样的精度足以让系统在执行时间差有限的情况下选择最有效的方案。如果分析模型与离散体处理策略相结合,误差可以降低到14%。

cost model可用于多种情况,从估计给定集群和应用程序配置的查询执行时间,到清楚地了解不同配置下的查询性能。这些是工作负载管理、容量规划和系统调优中的关键信息。我们的成本模型也是将Catalyst转变为完全基于成本的优化器的第一步,即使考虑了自适应执行,也可以比较不同物理计划的执行成本。

本文的主要贡献有:(1)建立了覆盖GPSJ查询表达性的分析成本模型;(2)大量的测试(总的来说,我们运行了1000多个查询),从几个不同的角度分析了它的准确性。对3个基准和2个群集进行了测试。

论文结构如下:第2节报道相关文献;第3节提供了Spark的背景知识,并描述了如何在Spark上执行GPSJ查询;成本模型的定义见第4节和第5节;第6节报告实验结果;第七部分,得出结论。

2 Related Work

关于预测SQL引擎性能的成本模型的文献可以追溯到70年代中期,当时第一个查询优化器被开发出来。在[5]中,作者提出了关系DBMS System R的成本模型:考虑了过滤、投影和连接操作。根据获取的磁盘页面计算成本,并对谓词选择性进行了一组基本假设。集中式SQL引擎的成本模型的有效性取决于可用统计数据的质量。例如,从基本属性和表基数转向直方图[6],可以在存在倾斜分布数据的情况下提高选择性谓词的准确性。当从集中式dbms传递到分布式dbms[7]时,还必须考虑通信时间。网络的拓扑结构在定义不同成本组成部分的权重方面起着核心作用:虽然传输成本在广域网中通常占主导地位[8],但为LAN设计的成本模型也必须考虑本地访问成本。并行dbms又向前迈进了一步,它要求成本模型考虑到资源争用和数据依赖[9]。

面向大数据平台的SQL引擎数量在不断增长。Hive是第一个提供类似sql的查询语言的解决方案,称为HiveQL[10]。Hive将HiveQL查询编译成一系列的MapReduce作业,这意味着高延迟。为了解决这个问题,已经有多个方向的解决方案被探索了:Tez可以将定向无环图(dag)作为单个作业运行,从而减少启动作业的延迟;很快,使用传统的MPP DBMS运行时而不是mapreduce。Spark SQL依赖于Catalyst优化器将SQL查询转换为可在Spark通用引擎上执行的优化dag,其执行速度比MapReduce快得多。本文分析的Catalyst版本主要是基于规则的;自2.3.0发布以来,Catalyst利用了统计数据和一个简单的成本模型。例如,对于连接排序,成本函数根据返回的行数估计逻辑成本

我们强调,我们提出的不是一个基于成本的优化器,而是一个成本模型。基于成本的优化器不必计算查询的全部成本,无论是绝对成本还是相对成本(例如,查询计划1优于查询计划2)。此外,基于成本的优化器实现查询计划转换(通常基于规则),该转换利用特定查询部分(例如,连接)的成本来做出选择并创建优化计划。相反,我们的成本模型根据Catalyst(即Spark查询优化器)提供的物理计划计算绝对查询成本(以秒为a单位)。在第6.5节中,我们证明了我们的成本模型返回的绝对成本足够精确,可以在可行的执行计划中选择最佳执行计划。关闭循环和更改实际的Spark计划不在本文的讨论范围之内。参考Hive[11],我们的成本模型与Hive优化器计算的成本之间的主要区别是:

  • Hive不计算整个查询成本,而是计算它正在优化的特定部分的成本。

  • Hive成本是在物理操作树上计算的,忽略了集群配置。因此。对于不同的集群和使用不同资源的执行,优化器的选择是相同的。

  • Hive对读写磁盘吞吐量做了更简单的假设,这些假设是固定的,并且不随磁盘上并发的进程而变化。

  • Hive优化器支持更大的SQL操作符集,例如UNION ALL和OUTER JOIN,这些操作符不能被建模为GPSJ。

相似性方面,我们采用了相同的Hive统计数据。特别是,Hive考虑表基数和属性基数,而不考虑属性值分布。Impala和Spark的优化器做出了类似的假设,除了Spark在其2018年版本(ver. 2.3.0)开始收集直方图[12]。与Spark不同的是,MapReduce的成本模型相关的研究工作和结果要多得多[13]。在[14]中,作者提出了MapReduce的分析模型,该模型除了考虑任务执行时间外,还考虑了并行执行造成的延迟:由于共享资源的占用而导致的排队延迟和由于任务之间的优先约束而导致的同步延迟。Herodotou[4]提出了一个性能模型来描述Hadoop 1.x中MapReduce作业的执行情况。这个性能模型描述了在更细粒度的map和reduce任务内的数据流和成本信息。根据Herodotou的模型,整个作业的执行时间是所有map和reduce阶段的成本之和,没有考虑同步和排队延迟。与前一个模型类似,我们的模型在重点考虑了单个执行阶段的细节,并通过考虑管道和资源争用来考虑排队和同步延迟。Spark SQL上下文中出现了许多差异:参考[14],任务之间的优先图来自输入中提供的查询的物理执行树,并且必须根据Spark如何实现SQL操作符来分析。考虑到Spark使用一组有限且已知的任务类型来执行GPSJ查询,并且DB统计数据是可用的,我们能够提供它们的详细建模。与[4]不同的是,我们不需要一个通用的工作概况来描述任务类型。这是因为,与一般的MapReduce作业不同,我们建模的SQL查询具有查询执行计划的详细描述,以及允许我们分析导出这些参数的DB统计信息。考虑到Spark使用一组有限且已知的任务类型来执行GPSJ查询,并且DB统计数据是可用的,我们能够提供它们的详细建模。与[4]不同的是,我们不需要一个通用的工作概况来描述任务类型。这是因为,与一般的MapReduce作业不同,我们建模的SQL查询具有查询执行计划的详细描述,以及允许我们分析导出这些参数的DB统计信息。

3 Background and Spark Basics

Spark是一个与Hadoop兼容的并行计算框架。Spark的架构由一个driver和一组executor组成。driver与集群资源管理器(Yarn)协商资源,并在executos之间分配。executors负责对数据执行操作。数据在整个集群中分布,并以弹性分布式数据集RDD的形式组织。一个RDD是一组不可变的分布式元素,被称为分区,可以被并行处理。分区可以来自于存储(HDFS),也可以是之前操作的结果(内存中)。Spark提供了一组丰富的操作符来操作后期的RDD。操作符可以分为transformation和action。transformation可以在每个RDD分区上的内存中执行,而action要么将结果返回给驱动程序,要么使用shuffle将分布在几个RDD分区中的数据组合在一起。除了RDD之外,Spark还引入了操作的lazy evaluation估概念,即Spark不会立即计算操作结果,但它会跟踪应用于RDD的操作序列,直到触发一个操作。Spark计算范式通过启用内存中的流水线(根据Volcano模型[17])来克服基于MapReduce的后续转换,这些转换不需要shuffle。

在最高的抽象层次上,Spark计算被组织在job中:job是在RDD请求action时创建的;它由称为stage的更简单的执行单元组成,通过有向无环图连接在一起。stage仍然是一个逻辑工作单元,因为它是由应用于输入RDD的transformation管道组成的。用于在每个RDD分区上执行一个stage的物理工作单元称为task。任务分布在集群上并并行执行。

Spark SQL[2]是Spark的模块,它可以对存储在Hadoop中的结构化数据执行SQL查询。它为数据提供了一个关系抽象层

### 使用 Apache Spark 进行交通流量数据分析 #### 初始化 Spark 环境 在使用 Apache Spark 处理交通流量数据前,需初始化 `SparkContext` 对象作为与 Spark 集群通信的入口点。以下是一个 Python 示例代码来初始化 Spark 上下文环境[^3]: ```python from pyspark import SparkConf, SparkContext conf = SparkConf().setAppName("TrafficFlowAnalysis").setMaster("local[*]") sc = SparkContext.getOrCreate(conf=conf) ``` 此代码片段设置了应用程序名称为 `"TrafficFlowAnalysis"` 并配置本地运行模式。 --- #### 数据加载与预处理 假设交通流量数据存储在一个 CSV 文件中,每条记录包含时间戳、位置坐标、车辆数量等字段。可以利用 Spark 的 DataFrame API 加载并解析这些数据。下面展示了一个简单的读取和转换过程: ```python from pyspark.sql import SQLContext sql_context = SQLContext(sc) # 假设文件路径为 'traffic_data.csv' data_path = "traffic_data.csv" df = sql_context.read.format("csv").option("header", "true").load(data_path) # 转换列类型 (例如将字符串型的时间戳转为 Timestamp 类型) from pyspark.sql.functions import col df = df.withColumn("timestamp", col("timestamp").cast("timestamp")) df = df.withColumn("vehicle_count", col("vehicle_count").cast("int")) # 查看 Schema 和前几行数据 print(df.schema.json()) df.show(5) ``` 通过以上步骤完成数据加载与初步清洗工作[^1]。 --- #### 实时流式处理 如果需要对实时传入的交通流量数据进行分析,则可采用 **Spark Streaming** 来实现。该模块会把输入的数据源划分为一系列短时间段的小批量(mini-batches),从而允许基于批处理的方式执行复杂的逻辑运算[^1]。这里给出一个基础框架例子: ```python from pyspark.streaming import StreamingContext ssc = StreamingContext(sc, batchDuration=10) # 设置每隔 10 秒钟接收一批新数据 def process_batch(time, rdd): print(f"Processing data at {time}") if not rdd.isEmpty(): records_df = ssc.sparkSession.createDataFrame(rdd) perform_analysis(records_df) dstream = ssc.socketTextStream("localhost", port_number).map(lambda line: parse_line(line)) dstream.foreachRDD(process_batch) ssc.start() ssc.awaitTermination() ``` 在此脚本里定义了自定义函数 `process_batch()` ,当有新的 RDD 到达时调用它来进行进一步的操作;而实际业务逻辑则封装进了名为 `perform_analysis()` 的方法之中[^2]。 --- #### 应用机器学习建模预测拥堵状况 为了更深入挖掘隐藏规律或者对未来趋势做出推测,还可以借助 MLlib 完成诸如线性回归之类的监督学习任务。比如尝试构建一个模型用来估计特定路段未来一段时间内的平均车速变化情况: ```python from pyspark.ml.feature import VectorAssembler from pyspark.ml.regression import LinearRegression assembler = VectorAssembler(inputCols=["hour_of_day", "day_of_week"], outputCol="features") transformed_df = assembler.transform(df.select("hour_of_day", "day_of_week", "avg_speed_kph")) lr_model = LinearRegression(featuresCol='features', labelCol='avg_speed_kph').fit(transformed_df) predictions = lr_model.transform(transformed_df) predictions.select('prediction','avg_speed_kph').show() ``` 上述代码展示了如何组合多个特征向量以及训练简单线性回归模型的过程[^1]。 --- #### 图结构网络优化研究 对于某些复杂场景下的全局最优解寻找问题来说,可能需要用到 GraphX 工具包去解决最短路径查找之类的问题。例如给定一张城市道路网拓扑图 G=(V,E),其中顶点 V 表示交叉路口节点集合,边 E 表示连接两处地点之间的街道关系权重 w(e)=length(e)+cost(e) 。那么就可以按照如下方式求得任意两点间距离最小值: ```python edges_rdd = sc.parallelize([(src_node_id,dst_node_id,wgt)]) vertices_rdd = sc.parallelize([(node_id,{"name": name})]) graph = GraphX(vertices_rdd, edges_rdd) shortest_paths_result = graph.shortestPaths([start_vertex]) for vid,sps in shortest_paths_result.collectAsMap().items(): pass ``` 此处仅列举部分核心语句示意用途[^4]。 ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值