Spark: a little summary

news2024/11/19 11:18:55

转眼写spark一年半了,从之前写机器学习组件、做olap到后面做图计算,一直都是用的spark,惭愧的是没太看过里面的源码。这篇文章的目的是总结一下Spark里面比较重要的point,重点部分会稍微看一下源代码,因为spark是跟clickhouse同级别的东西,复杂度也是相当的高,因为时间原因,本文也不会过度地进行展开。
只能说:源码用时方恨少。以前在写spark的时候没看源码实现真的挺遗憾的,现在也没那么多时间让我一点点细看了,之后稳定下来之后希望能有机会吧。

数据抽象

RDD

RDD 是一种抽象,是 Spark 对于分布式数据集的抽象,它用于囊括所有内存中和磁盘中的分布式数据实体。
数据分片(Partitions)是 RDD 抽象的重要属性之一。
在初步认识了 RDD 之后,接下来咱们换个视角,从 RDD 的重要属性出发,去进一步深入理解 RDD。
要想吃透 RDD,我们必须掌握它的 4 大属性:

  • partitions:数据分片(由partitioner决定)
  • partitioner:分片切割规则
  • dependencies:RDD 依赖
  • compute:转换函数

在 RDD 的编程模型中,一共有两种算子,Transformations 类算子和 Actions 类算子。开发者需要使用 Transformations 类算子,定义并描述数据形态的转换过程,然后调用 Actions 类算子,将计算结果收集起来、或是物化到磁盘。
image.png
在 Spark 中,创建 RDD 的典型方式有两种:

  • 通过 SparkContext.parallelize 在内部数据之上创建 RDD;
  • 通过 SparkContext.textFile 等 API 从外部数据创建 RDD。

使用分区变量而非RDD变量

在下面的例子中,RDD每一条都会产生一个MD5对象,这是没必要的。

// 把普通RDD转换为Paired RDD
 
import java.security.MessageDigest
 
val cleanWordRDD: RDD[String] = _ 
 
val kvRDD: RDD[(String, Int)] = cleanWordRDD.map{ word =>
  // 获取MD5对象实例
  val md5 = MessageDigest.getInstance("MD5")
  // 使用MD5计算哈希值
  val hash = md5.digest(word.getBytes).mkString
  // 返回哈希值与数字1的Pair
  (hash, 1)
}

使用mapPartitions可以以数据分区为粒度的数据转换:

// 把普通RDD转换为Paired RDD
 
import java.security.MessageDigest
 
val cleanWordRDD: RDD[String] = _ // 请参考第一讲获取完整代码
 
val kvRDD: RDD[(String, Int)] = cleanWordRDD.mapPartitions( partition => {
  // 注意!这里是以数据分区为粒度,获取MD5对象实例
  val md5 = MessageDigest.getInstance("MD5")
  val newPartition = partition.map( word => {
  // 在处理每一条数据记录的时候,可以复用同一个Partition内的MD5对象
    (md5.digest(word.getBytes()).mkString,1)
  })
  newPartition
})

DataFrame

进程模型

image.png
对于一个完整的 RDD,每个 Executors 负责处理这个 RDD 的一个数据分片子集。每当任务执行完毕,Executors 都会及时地与 Driver 进行通信、汇报任务状态。Driver 在获取到 Executors 的执行进度之后,结合计算流图的任务拆解,依次有序地将下一阶段的任务再次分发给 Executors 付诸执行,直至整个计算流图执行完毕。

调度模型

image.png
DAGScheduler 的主要职责有三个:

  • 根据用户代码构建 DAG;
  • 以 Shuffle 为边界切割 Stages;
  • 基于 Stages 创建 TaskSets,并将 TaskSets 提交给 TaskScheduler 请求调度。

SchedulerBackend 用一个叫做 ExecutorDataMap 的数据结构,来记录每一个计算节点中 Executors 的资源状态。这里的 ExecutorDataMap 是一种 HashMap,它的 Key 是标记 Executor 的字符串,Value 是一种叫做 ExecutorData 的数据结构。
ExecutorData 用于封装 Executor 的资源状态,如 RPC 地址、主机地址、可用 CPU 核数和满配 CPU 核数等等,它相当于是对 Executor 做的“资源画像”。
WorkerOffer 封装了 Executor ID、主机地址和 CPU 核数,它用来表示一份可用于调度任务的空闲资源。
对于给定的 WorkerOffer,TaskScheduler 是按照任务的本地倾向性,来遴选出 TaskSet 中适合调度的 Tasks。
像上面这种定向到计算节点粒度的本地性倾向,Spark 中的术语叫做 NODE_LOCAL。除了定向到节点,Task 还可以定向到进程(Executor)、机架、任意地址,它们对应的术语分别是 PROCESS_LOCAL、RACK_LOCAL 和 ANY。
对于倾向 PROCESS_LOCAL 的 Task 来说,它要求对应的数据分区在某个进程(Executor)中存有副本;而对于倾向 RACK_LOCAL 的 Task 来说,它仅要求相应的数据分区存在于同一机架即可。ANY 则等同于无定向,也就是 Task 对于分发的目的地没有倾向性,被调度到哪里都可以。
image.png
总结
任务调度分为如下 5 个步骤:

  1. DAGScheduler 以 Shuffle 为边界,将开发者设计的计算图 DAG 拆分为多个执行阶段 Stages,然后为每个 Stage 创建任务集 TaskSet。
  2. SchedulerBackend 通过与 Executors 中的 ExecutorBackend 的交互来实时地获取集群中可用的计算资源,并将这些信息记录到 ExecutorDataMap 数据结构。
  3. 与此同时,SchedulerBackend 根据 ExecutorDataMap 中可用资源创建 WorkerOffer,以 WorkerOffer 为粒度提供计算资源。
  4. 对于给定 WorkerOffer,TaskScheduler 结合 TaskSet 中任务的本地性倾向,按照 PROCESS_LOCAL、NODE_LOCAL、RACK_LOCAL 和 ANY 的顺序,依次对 TaskSet 中的任务进行遍历,优先调度本地性倾向要求苛刻的 Task。
  5. 被选中的 Task 由 TaskScheduler 传递给 SchedulerBackend,再由 SchedulerBackend 分发到 Executors 中的 ExecutorBackend。Executors 接收到 Task 之后,即调用本地线程池来执行分布式任务。
  6. 一个job由action算子来触发,每个job又会根据shuffle情况划分出多个stage,每个stage中又会划分出多个task,再根据taskScheduler分配到各个Excecutor。

Shuffle

Shuffle 文件的生成,是以 Map Task 为粒度的,Map 阶段有多少个 Map Task,就会生成多少份 Shuffle 中间文件。
image.png
shuffle文件包括两个文件,**一个是记录(Key,Value)键值对的 data 文件,另一个是记录键值对所属 Reduce Task 的 index 文件。(典中典的列式存储)**换句话说,index 文件标记了 data 文件中的哪些记录,应该由下游 Reduce 阶段中的哪些 Task(简称 Reduce Task)消费。
假设 Reduce 阶段有 N 个 Task,这 N 个 Task 对应着 N 个数据分区,那么在 Map 阶段,每条记录应该分发到哪个 Reduce Task,是由下面的公式来决定的。

P = Hash(Record Key) % N

shuffle生成过程
image.png
在生成中间文件的过程中,**Spark 会借助一种类似于 Map 的数据结构,来计算、缓存并排序数据分区中的数据记录。**这种 Map 结构的 Key 是(Reduce Task Partition ID,Record Key),而 Value 是原数据记录中的数据值,如图中的“内存数据结构”所示。
对于数据分区中的数据记录,Spark 会根据我们前面提到的公式 1 逐条计算记录所属的目标分区 ID,然后把主键(Reduce Task Partition ID,Record Key)和记录的数据值插入到 Map 数据结构中。当 Map 结构被灌满之后,Spark 根据主键对 Map 中的数据记录做排序,然后把所有内容溢出到磁盘中的临时文件,如图中的步骤 1 所示。
随着 Map 结构被清空,Spark 可以继续读取分区内容并继续向 Map 结构中插入数据,直到 Map 结构再次被灌满而再次溢出,如图中的步骤 2 所示。就这样,如此往复,直到数据分区中所有的数据记录都被处理完毕。
到此为之,磁盘上存有若干个溢出的临时文件,而内存的 Map 结构中留有部分数据,Spark 使用归并排序算法对所有临时文件和 Map 结构剩余数据做合并,分别生成 data 文件、和与之对应的 index 文件,如图中步骤 4 所示。Shuffle 阶段生成中间文件的过程,又叫 Shuffle Write。

数据聚合

groupbykey的全量shuffle开销很大(不做map端聚合,只做reduce端聚合),因此普遍使用 reduceByKey、aggregateByKey 和 combineByKey而不是groupbykey。

  • reduceByKey 在落盘与分发之前,会先在 Shuffle 的 Map 阶段做初步的聚合计算。在 Map 阶段,reduceByKey 把 Key 同为 Streaming 的两条数据记录聚合为一条,聚合逻辑就是由函数 f 定义的、取两者之间 Value 较大的数据记录,这个过程我们称之为“Map 端聚合”。相应地,数据经由网络分发之后,在 Reduce 阶段完成的计算,我们称之为“Reduce 端聚合”。reduceByKey 算子的局限性,在于其 Map 阶段与 Reduce 阶段的计算逻辑必须保持一致,这个计算逻辑统一由聚合函数 f 定义。当一种计算场景需要在两个阶段执行不同计算逻辑的时候,reduceByKey 就爱莫能助了。
  • aggregateByKey可以自定义map和reduce端聚合,是更加灵活的聚合算子

内存管理和存储系统

Shuffle 中间文件消耗的是节点磁盘,而广播变量主要占用节点的内存空间,RDD Cache 则是“脚踏两条船”,既可以消耗内存,也可以消耗磁盘。
image.png
Execution Memory 用来执行分布式任务。分布式任务的计算,主要包括数据的转换、过滤、映射、排序、聚合、归并等环节,而这些计算环节的内存消耗,统统来自于 Execution Memory。
Storage Memory 用于缓存分布式数据集,比如 RDD Cache、广播变量等等。RDD Cache 指的是 RDD 物化到内存中的副本。在一个较长的 DAG 中,如果同一个 RDD 被引用多次,那么把这个 RDD 缓存到内存中,往往会大幅提升作业的执行性能。
image.png
存储系统主要是由BlockManager模块负责:
image.png
BlockManager 的核心职责,在于管理数据块的元数据(Meta data),这些元数据记录并维护数据块的地址、位置、尺寸以及状态。
image.png

Cache

image.png
cache 函数实际上会进一步调用 persist(MEMORY_ONLY)来完成计算

数据的准备、重分配和持久化

这部分相关的算子如下:
image.png
首先,在数据准备阶段,union 与 sample 用于对不同来源的数据进行合并与拆分。
我们从左往右接着看,接下来是数据预处理环节。较为均衡的数据分布,对后面数据处理阶段提升 CPU 利用率更有帮助,可以整体提升执行效率。那这种均衡要怎么实现呢?没错,这时就要 coalesce 与 repartition 登场了,它们的作用就是重新调整 RDD 数据分布。
在数据处理完毕、计算完成之后,我们自然要对计算结果进行收集。Spark 提供了两类结果收集算子,一类是像 take、first、collect 这样,把结果直接收集到 Driver 端;另一类则是直接将计算结果持久化到(分布式)文件系统,比如咱们这一讲会提到的 saveAsTextFile。

  • RDD 的 sample 算子用于对 RDD 做随机采样,从而把一个较大的数据集变为一份“小数据”。相较其他算子,sample 的参数比较多,分别是 withReplacement、fraction 和 seed。因此,要在 RDD 之上完成数据采样,你需要使用如下的方式来调用 sample 算子:sample(withReplacement, fraction, seed)。
  • 开发者可以使用 repartition 算子随意调整(提升或降低)RDD 的并行度,而 coalesce 算子则只能用于降低 RDD 并行度。repartition和coalesce相比较,repartition由于引入了shuffle机制,对数据进行打散,混洗,重新平均分配,所以repartition操作较重,但是数据分配均匀。而coalesce只是粗力度移动数据,没有平均分配的过程,会导致数据分布不均匀,在计算时出现数据倾斜。

image.png

广播变量和累加器

  • 广播变量的分发不以task为粒度,而是以Executor为粒度,这样就减少了变量分发的开销
  • 累加器用于存储全局变量

配置项

image.png

Catalyst

Catalyst 优化器,它的职责在于创建并优化执行计划,它包含 3 个功能模块,分别是创建语法树并生成执行计划、逻辑阶段优化和物理阶段优化。
在 Catalyst 优化环节,Spark SQL 首先把用户代码转换为 AST 语法树,又叫执行计划,然后分别通过逻辑优化和物理优化来调整执行计划。逻辑阶段的优化,主要通过先验的启发式经验,如谓词下推、列剪枝,对执行计划做优化调整。而物理阶段的优化,更多是利用统计信息,选择最佳的执行机制、或添加必要的计算节点。

Tungsten

Tungsten 用于衔接 Catalyst 执行计划与底层的 Spark Core 执行引擎,它主要负责优化数据结果与可执行代码。
Tungsten 设计并实现了一种叫做 Unsafe Row 的二进制数据结构。Unsafe Row 本质上是字节数组,它以极其紧凑的格式来存储 DataFrame 的每一条数据记录,大幅削减存储开销,从而提升数据的存储与访问效率。

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

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

相关文章

HL祭记汇

一.写在前面 如果说廿四10天集训,对于我,是完成了从入门到蒟蒻的蜕变,那么,HL7天,可以说是真正成为了OIer,虽然是被小学生、初中生(南方的)薄纱的那种高中OIer…… 二.目录 Day 1…

微服务三十五关

1.微服务有什么好处? 微服务优点很多,但是我们通常说一个东西好肯定会跟另一个东西比较, 通常说微服务好会和单体项目进行比较。以下是微服务相对于单体项目的一些显著好处: 首先,让我们讨论单体项目的一些主要缺点&a…

IDEA生成Java Doc帮助文档

使用场景 使用IDEA(本次使用2020.3版)将自己写的常用的工具类打成jar包,安装到maven本地仓库,最后生成对应的doc参考文档。 操作流程 方法一 选中项目 右键 show in Explor,如下图: 选中地址栏 cmd 输入…

C#,计算几何,计算机图形学(Computer Graphics)洪水填充算法(Flood Fill Algorithm)与源代码

1 泛洪填充算法(Flood Fill Algorithm) 泛洪填充算法(Flood Fill Algorithm) ,又称洪水填充算法,是在很多图形绘制软件中常用的填充算法,最熟悉不过就是 windows 自带画图软件的油漆桶功能。 2 源程序 using System; using System.Collecti…

【LeetCode刷题笔记】242.有效的字母异位词

创作不易&#xff0c;本篇文章如果帮助到了你&#xff0c;还请点赞 关注支持一下♡>&#x16966;<)!! 主页专栏有更多知识&#xff0c;如有疑问欢迎大家指正讨论&#xff0c;共同进步&#xff01; 更多算法知识专栏&#xff1a;算法分析&#x1f525; 给大家跳段街舞感谢…

vue手写卡片切换,并且点击获取到卡片信息

需求&#xff1a;做一个卡片样式的列表&#xff0c;之后有一些基本信息&#xff0c;之后卡片选中后样式不一样&#xff0c;默认选中第一个卡片&#xff0c;点击卡片后可以获取到卡片的信息 一、效果 二、关键代码 index默认重0开始,activeTable默认为0,0-0等于0&#xff0c;但…

vscode与vue环境配置

一、下载并安装VScode 安装VScode 官网下载 二、配置node.js环境 安装node.js 官网下载 会自动配置环境变量和安装npm包(npm的作用就是对Node.js依赖的包进行管理)&#xff0c;此时可以执行 node -v 和 npm -v 分别查看node和npm的版本号&#xff1a; 配置系统变量 因为在执…

【openGL教程08】着色器(02)

LearnOpenGL - Shaders 一、说明 着色器是openGL渲染的重要内容&#xff0c;客户如果想自我实现渲染灵活性&#xff0c;可以用着色器进行编程&#xff0c;这种程序小脚本被传送到GPU的显卡内部&#xff0c;起到动态灵活的着色作用。 二、着色器简述 正如“Hello Triangle”一章…

[NCTF2019]True XML cookbook --不会编程的崽

题目的提示很明显了&#xff0c;就是xxe攻击&#xff0c;直接抓包。 <?xml version "1.0"?> <!DOCTYPE ANY [ <!ENTITY xxe SYSTEM "file:///etc/passwd" > ]> <user><username> &xxe; </username><passwor…

【蓝桥杯省赛真题26】python整数逆序输出 青少年组蓝桥杯比赛python编程省赛真题解析

目录 python整数逆序输出 一、题目要求 1、编程实现 2、输入输出 二、算法分析 三、程序代码 四、程序说明 五、运行结果 六、考点分析 七、 推荐资料 1、蓝桥杯比赛 2、考级资料 3、其它资料 python整数逆序输出 第十二届蓝桥杯python比赛省赛真题 一、题目要求…

java基础-正则表达式+文件操作+内置包装类

目录 正则表达式去除字符串前后空格&#xff1a;去除每一行中首尾的空格去除开头的 数字_ 文件操作打印当前项目路径获取文件的上级目录/和\读取文件 内置包装类System类常用方法 Number类Integer类常用方法Float和Double 正则表达式 去除字符串前后空格&#xff1a; str.tri…

Mamba详细介绍和RNN、Transformer的架构可视化对比

Transformer体系结构已经成为大型语言模型(llm)成功的主要组成部分。为了进一步改进llm&#xff0c;人们正在研发可能优于Transformer体系结构的新体系结构。其中一种方法是Mamba&#xff08;一种状态空间模型&#xff09;。 Mamba: Linear-Time Sequence Modeling with Select…

Stable Diffusion 3 Early Preview发布

2月22日&#xff0c;Stability AI 发布了 Stable Diffusion 3 early preview&#xff0c;这是一种开放权重的下一代图像合成模型。据报道&#xff0c;它继承了其前身&#xff0c;生成了详细的多主题图像&#xff0c;并提高了文本生成的质量和准确性。这一简短的公告并未附带公开…

【Java】RestClient的使用

RestClient的使用 先导入Maven坐标&#xff0c;要和elasticsearch和kibana的版本保持一致 <dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.12.1<…

python安装与配置2024最新版

对python不熟悉的可以去看看这篇文章python介绍 pytho安装 来到Python官网&#xff1a;https://www.python.org/ 然后 选着download列表下的windows , 然后加进入python各个版本的下载界面 可以看到截止2024年2月22日,最新版是3.12.2 但是我们一般下载稳定版 如下,点击连接进…

普中51单片机学习(DS1302)

DS1302时钟 DS1302实时时钟具有能计算2100年之前的秒、分、时、日、日期、星期、月、年的能力&#xff0c;还有闰年调整的能力。内部含有31个字节静态RAM&#xff0c;可提供用户访问。采用串行数据传送方式&#xff0c;使得管脚数量最少&#xff0c;简单SPI 3线接口。工作电压…

使用LinkedList实现堆栈及Set集合特点、遍历方式、常见实现类

目录 一、使用LinkedList实现堆栈 堆栈 LinkedList实现堆栈 二、集合框架 三、Set集合 1.特点 2.遍历方式 3.常见实现类 HashSet LinkedHashSet TreeSet 一、使用LinkedList实现堆栈 堆栈 堆栈&#xff08;stack&#xff09;是一种常见的数据结构&#xff0c;一端…

[Java基础揉碎]idea工具

目录 调整导航菜单区域的字体大小: 调整代码区域的字体大小: 代码字体变粗: 调整颜色主题: 调整全局字符编码(Project Encoding:项目的字符编码) : 常用的快捷键(使用的windows快捷键主题): ​编辑 ​编辑 ​编辑 自己配置:​编辑 模版快捷键: 导入该行需要的类:…

【JavaEE】_tomcat的安装与使用

目录 1. Tomcat简介 2. Tomcat安装 2.1 下载Tomcat并解压缩 2.2 启动Tomcat 2.2.1 Tomcat乱码问题 2.2.2 Tomcat闪退问题 2.3 访问Tomcat欢迎页面 3. 使用Tomcat部署前端代码 3.1 路径匹配 3.2 文件路径访问与网络访问 4. 静态页面与动态页面 5. 基于tomcat的网站后…

Django学习笔记-forms使用

1.创建forms.py文件,导入包 from django import forms from django.forms import fields from django.forms import widgets2. 创建EmployeeForm,继承forms.Form 3.创建testform.html文件 4.urls.py添加路由 5.views中导入forms 创建testform,编写代码 1).如果请求方式为GET,…