09-Spark架构

相比MapReduce僵化的Map与Reduce分阶段计算,Spark计算框架更有弹性和灵活性,运行性能更佳。

1 Spark的计算阶段

  • MapReduce一个应用一次只运行一个map和一个reduce
  • Spark可根据应用复杂度,分割成更多的计算阶段(stage),组成一个DAG,Spark任务调度器可根据DAG依赖关系执行计算阶段

逻辑回归机器学习性能Spark比MapReduce快100多倍。因某些机器学习算法可能需大量迭代计算,产生数万个计算阶段,这些计算阶段在一个应用中处理完成,而不像MapReduce需要启动数万个应用,因此运行效率极高。

DAG,不同阶段的依赖关系有向,计算过程只能沿依赖关系方向执行,被依赖的阶段执行完成前,依赖的阶段不能开始执行。该依赖关系不能有环形依赖,否则就死循环。

典型的Spark运行DAG的不同阶段:

整个应用被切分成3个阶段,阶段3依赖阶段1、2,阶段1、2互不依赖。Spark执行调度时,先执行阶段1、2,完成后,再执行阶段3。对应Spark伪代码:

rddB = rddA.groupBy(key)
rddD = rddC.map(func)
rddF = rddD.union(rddE)
rddG = rddB.join(rddF)

所以Spark作业调度执行核心是DAG,整个应用被切分成数个阶段,每个阶段的依赖关系也很清楚。根据每个阶段要处理的数据量生成任务集合(TaskSet),每个任务都分配一个任务进程去处理,Spark就实现大数据分布式计算。

负责Spark应用DAG生成和管理的组件是DAGScheduler:

  • DAGScheduler根据程序代码生成DAG
  • 然后将程序分发到分布式计算集群
  • 按计算阶段的先后关系调度执行

Spark划分计算阶段的依据

显然并非RDD上的每个转换函数都会生成一个计算阶段,如上4个转换函数,但只有3个阶段。

观察上面DAG图,计算阶段的划分就看出,当RDD之间的转换连接线呈现多对多交叉连接,就产生新阶段。一个RDD代表一个数据集,图中每个RDD里面都包含多个小块,每个小块代表RDD的一个分片。

一个数据集中的多个数据分片需进行分区传输,写到另一个数据集的不同分片,这种数据分区交叉传输操作,在MapReduce运行过程也看过。

这就是shuffle过程,Spark也要通过shuffle将数据重组,相同Key的数据放在一起,进行聚合、关联等操作,因而每次shuffle都产生新的计算阶段。这也是为什么计算阶段会有依赖关系,它需要的数据来源于前面一个或多个计算阶段产生的数据,必须等待前面的阶段执行完毕才能进行shuffle,并得到数据。

计算阶段划分依据是shuffle,而非转换函数的类型,有的函数有时有shuffle,有时无。如上图例子中RDD B和RDD F进行join,得到RDD G,这里的RDD F需要进行shuffle,RDD B不需要。

因为RDD B在前面一个阶段,阶段1的shuffle过程中,已进行数据分区。分区数目和分区K不变,无需再shuffle:

  • 这种无需进行shuffle的依赖,在Spark里称窄依赖
  • 需进行shuffle的依赖,称宽依赖

类似MapReduce,shuffle对Spark也重要,只有通过shuffle,相关数据才能互相计算。

既然都要shuffle,为何Spark更高效?

本质Spark算一种MapReduce计算模型的不同实现。Hadoop MapReduce简单粗暴根据shuffle将大数据计算分成Map、Reduce两阶段就完事。但Spark更细,将前一个的Reduce和后一个的Map连接,当作一个阶段持续计算,形成一个更优雅、高效地计算模型,其本质依然是Map、Reduce。但这种多个计算阶段依赖执行的方案可有效减少对HDFS的访问,减少作业的调度执行次数,因此执行速度更快。

不同于Hadoop MapReduce主要使用磁盘存储shuffle过程中的数据,Spark优先使用内存进行数据存储,包括RDD数据。除非内存不够用,否则尽可能使用内存, 这即Spark比Hadoop性能高。

2 Spark作业管理

Spark里面的RDD函数有两种:

  • 转换函数,调用后得到的还是RDD,RDD计算逻辑主要通过转换函数
  • action函数,调用后不再返回RDD。如count()函数,返回RDD中数据的元素个数
  • saveAsTextFile(path),将RDD数据存储到path路径

Spark的DAGScheduler遇到shuffle时,会生成一个计算阶段,在遇到action函数时,会生成一个作业(job)。

RDD里面的每个数据分片,Spark都会创建一个计算任务去处理,所以一个计算阶段含多个计算任务(task)。

作业、计算阶段、任务的依赖和时间先后关系:

横轴时间,纵轴任务。两条粗黑线之间是一个作业,两条细线之间是一个计算阶段。一个作业至少包含一个计算阶段。水平方向红色的线是任务,每个阶段由很多个任务组成,这些任务组成一个任务集合。

DAGScheduler根据代码生成DAG图后,Spark任务调度就以任务为单位进行分配,将任务分配到分布式集群的不同机器上执行。

3 Spark执行流程

Spark支持Standalone、Yarn、Mesos、K8s等多种部署方案,原理类似,仅不同组件的角色命名不同。

3.1 Spark cluster components

Spark应用程序启动在自己的JVM进程里(Driver进程),启动后调用SparkContext初始化执行配置和输入数据。SparkContext启动DAGScheduler构造执行的DAG图,切分成最小的执行单位-计算任务。

然后,Driver向Cluster Manager请求计算资源,用于DAG的分布式计算。Cluster Manager收到请求后,将Driver的主机地址等信息通知给集群的所有计算节点Worker。

Worker收到信息后,根据Driver的主机地址,跟Driver通信并注册,然后根据自己的空闲资源向Driver通报自己可以领用的任务数。Driver根据DAG图开始向注册的Worker分配任务。

Worker收到任务后,启动Executor进程执行任务。Executor先检查自己是否有Driver的执行代码,若无,从Driver下载执行代码,通过Java反射加载后开始执行。

4 Spark V.S Hadoop

4.1 个体对比

4.2 生态圈对比

4.3 MapReduce V.S Spark

4.4 优势

4.5 Spark 和 Hadoop 协作

5 总结

相比Mapreduce,Spark的主要特性:

  • RDD编程模型更简单
  • DAG切分的多阶段计算过程更快
  • 使用内存存储中间计算结果更高效

Spark在2012开始流行,那时内存容量提升和成本降低已经比MapReduce出现的十年前强了一个数量级,Spark优先使用内存的条件已成熟。

本文描述的内存模型自 Apache Spark 1.6+ 开始弃用,新的内存模型基于 UnifiedMemoryManager,并在这篇文章中描述。

在最近的时间里,我在 StackOverflow 上回答了一系列与 ApacheSpark 架构有关的问题。所有这些问题似乎都是因为互联网上缺少一份关于 Spark 架构的好的通用描述造成的。即使是官方指南也没有太多细节,当然也缺乏好的图表。《学习 Spark》这本书和官方研讨会的资料也是如此。

在这篇文章中,我将尝试解决这个问题,提供一个关于 Spark 架构的一站式指南,以及对其一些最受欢迎的概念问题的解答。这篇文章并不适合完全的初学者——它不会为你提供关于 Spark 主要编程抽象(RDD 和 DAG)的洞见,但是它要求你有这些知识作为先决条件。

从 http://spark.apache.org/docs/1.3.0/cluster-overview.html 上可用的官方图片开始:

Spark 架构官方:

如你所见,它同时引入了许多术语——“executor”,“task”,“cache”,“Worker Node”等等。当我开始学习 Spark 概念的时候,这几乎是互联网上唯一关于 Spark 架构的图片,现在情况也没有太大改变。我个人不是很喜欢这个,因为它没有显示一些重要的概念,或者显示得不是最佳方式。

让我们从头说起。任何,任何在你的集群或本地机器上运行的 Spark 过程都是一个 JVM 过程。与任何 JVM 过程一样,你可以用 -Xmx-Xms JVM 标志来配置它的堆大小。这个过程如何使用它的堆内存,以及它为什么需要它?以下是 JVM 堆内的 Spark 内存分配图表:

Spark 堆使用

默认情况下,Spark 以 512MB JVM 堆启动。为了安全起见,避免 OOM 错误,Spark 只允许使用堆的 90%,这由参数 spark.storage.safetyFraction 控制。好的,正如你可能已经听说 Spark 是一个内存中的工具,Spark 允许你将一些数据存储在内存中。如果你读过我这里的文章 https://0x0fff.com/spark-misconceptions/,你应该理解 Spark 并不是真的内存工具,它只是利用内存来缓存 LRU(http://en.wikipedia.org/wiki/Cache_algorithms)。所以一些内存是为你处理的数据缓存而保留的部分,这部分通常是安全堆的 60%,由 spark.storage.memoryFraction 参数控制。所以如果你想知道你可以在 Spark 中缓存多少数据,你应该取所有执行器的堆大小之和,乘以 safetyFractionstorage.memoryFraction,默认情况下,它是 0.9 * 0.6 = 0.54 或者让 Spark 使用的总的堆大小的 54%。

现在更详细地了解 shuffle 内存。它的计算方法为 “堆大小” * spark.shuffle.safetyFraction * spark.shuffle.memoryFractionspark.shuffle.safetyFraction 的默认值是 0.8 或 80%,spark.shuffle.memoryFraction 的默认值是 0.2 或 20%。所以最终你可以使用最多 0.8*0.2 = 0.16 或 JVM 堆的 16% 用于 shuffle。但是 Spark 如何使用这些内存呢?你可以在这里获取更多细节(https://github.com/apache/spark/blob/branch-1.3/core/src/main/scala/org/apache/spark/shuffle/ShuffleMemoryManager.scala),但总的来说,Spark 用这些内存进行它的 Shuffle。当 Shuffle 进行时,有时你也需要对数据进行排序。当你排序数据时,你通常需要一个缓冲区来存储排序后的数据(记住,你不能就地修改 LRU 缓存中的数据,因为它是用来稍后重用的)。所以它需要一些 RAM 来存储排序的数据块。如果你没有足够的内存来排序数据会怎样?有一系列通常被称为“外部排序”的算法(http://en.wikipedia.org/wiki/External_sorting)允许你进行分块数据的排序,然后再将最终结果合并起来。

我还没涵盖的 RAM 的最后部分是“unroll”内存。被 unroll 过程使用的 RAM 部分是 spark.storage.unrollFraction * spark.storage.memoryFraction * spark.storage.safetyFraction,默认值等于 0.2 * 0.6 * 0.9 = 0.108 或者堆的 10.8%。这是当你将数据块 unroll 到内存时可以使用的内存。为什么你需要 unroll 它呢?Spark 允许你以序列化和非序列化形式存储数据。序列化形式的数据不能直接使用,因此你需要在使用之前 unroll 它,所以这是用于 unroll 的 RAM。它与存储 RAM 共享,这意味着如果你需要一些内存来 unroll 数据,这可能会导致 Spark LRU 缓存中存储的一些分区被删除。

这很好,因为此刻你知道了什么是 Spark 过程以及它如何利用它的 JVM 过程的内存。现在让我们转到集群模式——当你启动一个 Spark 集群时,它实际上是什么样的呢?我喜欢 YARN,所以我将讲述它在 YARN 上是如何工作的,但是总的来说,对于任何你使用的集群管理器来说都是一样的:

在 YARN 上的 Spark 架构:

当你有一个 YARN 集群时,它有一个 YARN Resource Manager 守护进程,控制集群资源(实际上是内存)以及在集群节点上运行的一系列 YARN Node Managers,控制节点资源利用率。从 YARN 的角度来看,每个节点代表你有控制权的 RAM 池。当你向 YARN Resource Manager 请求一些资源时,它会给你提供你可以联系哪些 Node Managers 为你启动执行容器的信息。每个执行容器是一个具有请求堆大小的 JVM。JVM 位置由 YARN Resource Manager 选择,你无法控制它——如果节点有 64GB 的 RAM 被 YARN 控制(yarn-site.xml 中的 yarn.nodemanager.resource.memory-mb 设置)并且你请求 10 个执行器,每个执行器 4GB,它们所有的都可以容易地在一个 YARN 节点上启动,即使你有一个大集群。

当你在 YARN 之上启动 Spark 集群时,你指定了你需要的执行器数量(–num-executors 标志或 spark.executor.instances 参数)、每个执行器使用的内存量(–executor-memory 标志或 spark.executor.memory 参数)、每个执行器允许使用的核心数量(–executor-cores 标志或 spark.executor.cores 参数),以及为每个任务的执行专用的核心数量(spark.task.cpus 参数)。同时你还指定了驱动程序应用程序使用的内存量(–driver-memory 标志或 spark.driver.memory 参数)。

当你在集群上执行某事时,你的工作处理被分割成阶段,每个阶段又被分割成任务。每个任务分别被调度。你可以将每个作为执行者工作的 JVM 视为一个任务执行槽池,每个执行者会给你 spark.executor.cores / spark.task.cpus 执行槽供你的任务使用,总共有 spark.executor.instances 执行器。这是一个例子。有 12 个节点运行 YARN Node Managers 的集群,每个节点 64GB 的 RAM 和 32 个 CPU 核心(16 个物理核心与超线程)。这样,在每个节点上你可以启动 2 个执行器,每个执行器 26GB 的 RAM(为系统进程、YARN NM 和 DataNode 留下一些 RAM),每个执行器有 12 个核心用于任务(为系统进程、YARN NM 和 DataNode 留下一些核心)。所以总的来说你的集群可以处理 12 台机器 * 每台机器 2 个执行器 * 每个执行器 12 个核心 / 每个任务 1 个核心 = 288 个任务槽。这意味着你的 Spark 集群将能够并行运行多达 288 个任务,从而利用你在这个集群上拥有的几乎所有资源。你可以在这个集群上缓存数据的内存量是 0.9 * spark.storage.safetyFraction * 0.6 * spark.storage.memoryFraction * 12 台机器 * 每台机器 2 个执行器 * 每个执行器 26 GB = 336.96 GB。不算太多,但在大多数情况下它是足够的。

到目前为止效果很好,现在你知道了 Spark 如何使用它的 JVM 的内存以及你在集群上有哪些执行槽。正如你可能已经注意到的,我没有详细介绍“任务”究竟是什么。这将是下一篇文章的主题,但基本上它是 Spark 执行的一个单一工作单元,并作为 线程* 在执行器 JVM 中执行。这是 Spark 低作业启动时间的秘诀——在 JVM 中启动额外的线程比启动整个 JVM 快得多,而后者是在 Hadoop 中开始 MapReduce 作业时执行的。

现在让我们关注另一个叫做“partition”的 Spark 抽象。你在 Spark 中工作的所有数据都被分割成分区。一个单一的分区是什么,它是如何确定的?分区大小完全取决于你使用的数据源。对于大多数在 Spark 中读取数据的方法,你可以指定你想要在你的 RDD 中有多少分区。当你从 HDFS 读取一个文件时,你使用的是 Hadoop 的 InputFormat 来做到这一点。默认情况下,InputFormat 返回的每个输入分割都映射到 RDD 中的单个分区。对于 HDFS 上的大多数文件,每个输入分割生成一个对应于 HDFS 上存储的一个数据块的数据,大约是 64MB 或 128MB 的数据。大约,因为在 HDFS 中,数据是按照字节的确切块边界分割的,但是在处理时它是按照记录分割分割的。对于文本文件,分割字符是换行符,对于序列文件,是块末等等。这个规则的唯一例外是压缩文件——如果你有整个文本文件被压缩,那么它不能被分割成记录,整个文件将成为一个单一的输入分割,从而在 Spark 中成为一个单一的分区,你必须手动重新分区它。

现在我们所拥有的真的很简单——为了处理一个单独的数据分区,Spark 生成一个单一任务,这个任务在靠近你拥有的数据的位置(Hadoop 块位置,Spark 缓存的分区位置)的任务槽中执行。

参考

  • https://spark.apache.org/docs/3.2.1/cluster-overview.html
  • shuffle可以在这里找到
  • 新内存管理模型可以在这里找到

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/pingmian/29412.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

如何正确操作工业高温烤箱

高温烤箱广泛应用于陶瓷、丝印、汽车配件、电子、机电、通讯、化工、器材、印刷、制药、工业、橡胶、油漆、食品之烘烤、水份干燥、预热等用途。那么要想工业高温烤箱在使用的过程中能够正常运行,那么正确的操作是必不可少的, 1、防止触电:高…

教你一招,一键学会NAS磁盘“净身出户”的好方法!

在毕业季这个充满离别与新的开始的时刻,空气中似乎也弥漫着一种“断舍离”的氛围。就在这个特殊的季节里,我们迎来了618购物节,各种诱人的优惠活动如雨后春笋般涌现。铁威马618优惠不断!T系列部分低至六折! 在这个热闹…

超级会员卡积分收银小程序源码系统,在线充值+商家核销+在线下单 附带源代码+搭建部署教程

系统概述 在当今数字化快速发展的时代,移动支付已经成为人们生活中不可或缺的一部分。为了满足商家和消费者对于便捷、高效支付体验的需求,超级会员卡积分收银小程序源码系统应运而生。本文将深入介绍该源码系统的开发背景及其特色功能,附带…

录制视频软件哪个好?录制视频,4款好软件推荐

随着网络技术的飞速发展和社交媒体的普及,录制视频已经成为人们记录生活、分享知识和展示才华的重要方式。在众多录制视频软件中,如何挑选一款功能强大、操作简便的工具,成为了许多用户的难题。本文将为您推荐4款优秀的录制视频软件&#xff…

SpringMVC系列九: 数据格式化与验证及国际化

SpringMVC 数据格式化基本介绍基本数据类型和字符串自动转换应用实例-页面演示方式Postman完成测试 特殊数据类型和字符串自动转换应用实例-页面演示方式Postman完成测试 验证及国际化概述应用实例代码实现注意事项和使用细节 注解的结合使用先看一个问题解决问题 数据类型转换…

LearnOpenGL 及 ShaderToy 的 CMake 构建框架

文章目录 构建目标具体框架根目录src 目录app 目录import.cmake其他 CMake 函数 使用框架实际效果摄像机坐标变换使用 assimp 库加载模型shadertoy 测试 framebuffer 离屏渲染 其他 为了复习 OpenGL(主要是看到 shadertoy 上有好玩的着色器),…

C#开发-集合使用和技巧(六)特殊转换方法SelectMany的介绍和用法

介绍 SelectMany 方法在C#中用于将集合中的元素转换为其他类型的集合&#xff0c;并将这些集合扁平化为一个单一的序列。它是LINQ的一部分&#xff0c;允许你在一个序列上进行投影和过滤操作&#xff0c;然后将结果合并成一个序列。 方法定义 public static IEnumerable<…

城市行人感知新方法:基于音频的行人检测与预测

智慧城市的重要组成部分之一是部署传感器技术来监控和控制城市的各种服务和功能。城市使用各种传感器来评估城市服务的提供和获取方式&#xff0c;这有助于缓解瓶颈问题&#xff0c;并提前预警潜在的服务中断。了解城市服务需求的时间和空间变化有助于更好的资源利用、更公平的…

ionic 项目通过 android studio 打开报错 capacitor.settings.gradle 文件不存在

问题出现 原因分析 在程序相应的目录上面&#xff0c;没有找到对应的配置文件&#xff0c;但是这个文件不是我们自己生成的&#xff0c;而是通过 ionic 编译之后生成。 处理方案 先执行 ionic build&#xff0c;将 ionic 项目打包出来然后执行 npx cap sync 再使用 Android…

Financial Statement Analysis with Large Language Models论文精读

Financial Statement Analysis with Large Language Models 论文精读 文章目录 Financial Statement Analysis with Large Language Models 论文精读Abstract 核心速览研究细节baselineGPT与分析师对比人类分析师与 GPT 的互补性错误预测的来源增量信息增益 分析师出现偏差或分…

【YOLOv10改进[注意力]】在YOLOv10中使用注意力MLCA的实践+ 含全部代码和详细修改方式 + 手撕结构图 + 全网首发

本文将进行在YOLOv10中添加注意力MLCA的实践,助力YOLOv10目标检测效果的实践,文中含全部代码、详细修改方式以及手撕结构图。助您轻松理解改进的方法。 改进前和改进后的参数对比: 目录 一 MLCA 二 在YOLOv10中使用注意力MLCA的实践 1 整体修改

【CS.AL】算法核心之分治算法:从入门到进阶

文章目录 1. 概述2. 适用场景3. 设计步骤4. 优缺点5. 典型应用6. 题目和代码示例6.1 简单题目&#xff1a;归并排序6.2 中等题目&#xff1a;最近点对问题6.3 困难题目&#xff1a;分数背包问题 7. 题目和思路表格8. 总结References 1000.01.CS.AL.1.4-核心-DivedeToConquerAlg…

Python兴趣编程百例:手把手带你开发一个图片转字符图的小工具

在数字世界的无尽探索中&#xff0c;我们时常被那些看似平凡的技术所启发&#xff0c;它们如同星辰般点缀着我们的创意天空。今天&#xff0c;我突发奇想&#xff0c;想要用Python开发一个将图片转化为字符画的小工具。这不仅是一次技术的实践&#xff0c;更是一场艺术与科技的…

多客陪玩系统源码支持二次开发陪玩预约系统搭建,打造专业游戏陪玩平台

简述 随着电竞行业的快速发展&#xff0c;电竞陪玩APP正在逐渐成为用户在休闲娱乐时的首选。为了吸引用户和提高用户体验&#xff0c;电竞陪玩APP开发需要定制一些特色功能&#xff0c;并通过合适的盈利模式来获得收益。本文将为您介绍电竞陪玩APP开发需要定制的特色功能以及常…

LiveCharts2:简单灵活交互式且功能强大的.NET图表库

前言 之前的文章中提到过ScottPlot、与oxyplot&#xff0c;这两个是比较常用的.NET图表库&#xff0c;今天介绍一款新的.NET图表库&#xff1a;LiveCharts2。 LiveCharts2介绍 LiveCharts2 是一个现代化的数据可视化库&#xff0c;用于创建动态和交互式图表&#xff0c;支持…

一小时搞定JavaScript(2)——DOM与BOM的应用

前言,本篇文章是依据bilibili博主(波波酱老师)的学习笔记,波波酱老师讲的很好,很适合速成!!! 本篇文章会与java进行对比学习,因为JS中很多语法和java是相同的,所以大家最好熟悉Java语言后再来进行学习,效果更佳,见效更快. 文章目录 5.DOM和BOM5.1 DOM5.1.1传统元素获取5.1.2 C…

高考志愿填报,是选好专业,还是选好学校?过来人给你说说

分数限制下&#xff0c;选好专业还是选好学校&#xff1f; 到底是先选专业还是先选学校&#xff0c;是让考生及家长一直拿不准、辨不清的问题&#xff0c;是优先考虑学校还是专业&#xff0c;上了好学校&#xff0c;专业不喜欢就业前景不理想&#xff0c;怎么办&#xff1f;为…

【未来已来】AI大模型革命:向量数据库如何重塑智能世界?

在人工智能的浪潮中,向量数据库正成为推动AI大模型发展的幕后英雄。这不是简单的技术升级,而是一场关于智能未来的革命。本文将带您深入了解向量数据库如何成为AI大模型的核心竞争力,以及它如何助力我们在智能化的道路上加速前进。 向量数据库:AI大模型的心脏 想象一下…

vue echarts画多柱状图+多折线图

<!--多柱状图折线图--> <div class"echarts-box" id"multiBarPlusLine"></div>import * as echarts from echarts;mounted() {this.getMultiBarPlusLine() },getMultiBarPlusLine() {const container document.getElementById(multiBar…

图书管理系统代码(Java)

1、运行演示 QQ2024528-205028-HD 详细讲解在这篇博客&#xff1a;JavaSE&#xff1a;图书管理系统-CSDN博客 2、所建的包 3、Java代码 3.1 book包 3.1.1 Book类代码 package book;/*** Created with IntelliJ IDEA.* Description:* User: dings* Date: 2024-05-13* Time:…