理解RDD基本属性与Spark进程模型

💐💐扫码关注公众号,回复 spark 关键字下载geekbang 原价 90 元 零基础入门 Spark 学习资料💐💐

RDD 的 4 大属性:

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

从薯片的加工流程看 RDD 的 4 大属性:

在很久很久以前,有个生产桶装薯片的工坊,工坊的规模较小,工艺也比较原始。为了充分利用每一颗土豆、降低生产成本,工坊使用 3 条流水线来同时生产 3 种不同尺寸的桶装薯片。3 条流水线可以同时加工 3 颗土豆,每条流水线的作业流程都是一样的,分别是清洗、切片、烘焙、分发和装桶。其中,分发环节用于区分小、中、大号 3 种薯片,3 种不同尺寸的薯片分别被发往第 1、2、3 条流水线。具体流程如下图所示。

如果我们把每一条流水线看作是分布式运行环境的计算节点,用薯片生产的流程去类比 Spark 分布式计算,会有哪些有趣的发现呢?

每一种食材形态,如“带泥土豆”、“干净土豆”、“土豆片”等,都可以看成是一个个 RDD。而薯片的制作过程,实际上就是不同食材形态的转换过程。

起初,工人们从麻袋中把“带泥土豆”加载到流水线,这些土豆经过清洗之后,摇身一变,成了“干净土豆”。接下来,流水线上的切片机再把“干净土豆”切成“土豆片”,然后紧接着把这些土豆片放进烤箱。最终,土豆片烤熟之后,就变成了可以放心食用的即食薯片。不同食材形态之间的转换过程,与 Word Count 中不同 RDD 之间的转换过程如出一辙。

首先,沿着纵向,也就是从上到下的方向,去观察上图中土豆工坊的制作工艺。

对于每一种食材形态来说,流水线上都有多个实物与之对应,比如,“带泥土豆”是一种食材形态,流水线上总共有 3 颗“脏兮兮”的土豆同属于这一形态。如果把“带泥土豆”看成是 RDD 的话,那么 RDD 的 partitions 属性,囊括的正是麻袋里那一颗颗脏兮兮的土豆。同理,流水线上所有洗净的土豆,一同构成了“干净土豆”RDD 的 partitions 属性

再来看 RDD 的 partitioner 属性,这个属性定义了把原始数据集切割成数据分片的切割规则。在土豆工坊的例子中,“带泥土豆”RDD 的切割规则是随机拿取,也就是从麻袋中随机拿取一颗脏兮兮的土豆放到流水线上。后面的食材形态,如“干净土豆”、“土豆片”和“即食薯片”,则沿用了“带泥土豆”RDD 的切割规则。换句话说,后续的这些 RDD,分别继承了前一个 RDD 的 partitioner 属性。与众不同的是“分发的即食薯片”。显然,“分发的即食薯片”是通过对“即食薯片”按照大、中、小号做分发得到的。也就是说,对于“分发的即食薯片”来说,它的 partitioner 属性,重新定义了这个 RDD 数据分片的切割规则,也就是把先前 RDD 的数据分片打散,按照薯片尺寸重新构建数据分片。

数据分片的分布,是由 RDD 的 partitioner 决定的。因此,RDD 的 partitions 属性,与它的 partitioner 属性是强相关的。

接下来,横向地,也就是沿着从左至右的方向,再来观察土豆工坊的制作工艺。

流水线上的每一种食材形态,都是上一种食材形态在某种操作下进行转换得到的。比如,“土豆片”依赖的食材形态是“干净土豆”,这中间用于转换的操作是“切片”这个动作。回顾 Word Count 当中 RDD 之间的转换关系,我们也会发现类似的现象。

在数据形态的转换过程中,每个 RDD 都会通过 dependencies 属性来记录它所依赖的前一个、或是多个 RDD,简称“父 RDD”。与此同时,RDD 使用 compute 属性,来记录从父 RDD 到当前 RDD 的转换操作。

薯片的加工流程,与 RDD 的概念和 4 大属性是一一对应的:

  • 不同的食材形态,如带泥土豆、土豆片、即食薯片等等,对应的就是 RDD 概念;
  • 同一种食材形态在不同流水线上的具体实物,就是 RDD 的 partitions 属性;
  • 食材按照什么规则被分配到哪条流水线,对应的就是 RDD 的 partitioner 属性;
  • 每一种食材形态都会依赖上一种形态,这种依赖关系对应的是 RDD 中的 dependencies 属性;
  • 不同环节的加工方法对应 RDD 的 compute 属性。

进程模型

零基础入门 Spark 包含 sparkCore,sparkSql,sparkStreaming 全套零基础入门到精通教程。

在 Spark 的应用开发中,任何一个应用程序的入口,都是带有 SparkSession 的 main 函数。SparkSession 包罗万象,它在提供 Spark 运行时上下文的同时(如调度系统、存储系统、内存管理、RPC 通信),也可以为开发者提供创建、转换、计算分布式数据集(如 RDD)的开发 API。

不过,在 Spark 分布式计算环境中,有且仅有一个 JVM 进程运行这样的 main 函数,这个特殊的 JVM 进程,在 Spark 中有个专门的术语,叫作“Driver”。

Driver 最核心的作用在于,解析用户代码、构建计算流图,然后将计算流图转化为分布式任务,并把任务分发给集群中的执行进程交付运行。换句话说,Driver 的角色是拆解任务、派活儿,而真正干活儿的“苦力”,是执行进程。在 Spark 的分布式环境中,这样的执行进程可以有一个或是多个,它们也有专门的术语,叫作“Executor”。

分布式计算的核心是任务调度,而分布式任务的调度与执行,仰仗的是 Driver 与 Executors 之间的通力合作。

Driver 与 Executors:包工头与施工工人

简单来看,Driver 与 Executors 的关系,就像是工地上包工头与施工工人们之间的关系。包工头负责“揽活儿”,拿到设计图纸之后负责拆解任务,把二维平面图,细化成夯土、打地基、砌墙、浇筑钢筋混凝土等任务,然后再把任务派发给手下的工人。工人们认领到任务之后,相对独立地去完成各自的任务,仅在必要的时候进行沟通与协调。

其实不同的建筑任务之间,往往是存在依赖关系的,比如,砌墙一定是在地基打成之后才能施工,同理,浇筑钢筋混凝土也一定要等到砖墙砌成之后才能进行。因此,Driver 这个“包工头”的重要职责之一,就是合理有序地拆解并安排建筑任务。

再者,为了保证施工进度,Driver 除了分发任务之外,还需要定期与每个 Executor 进行沟通,及时获取他们的工作进展,从而协调整体的执行进度。在 Spark 的 Driver 进程中,DAGSchedulerTaskSchedulerSchedulerBackend 这三个对象通力合作,依次完成分布式任务调度的 3 个核心步骤,也就是:

  1. 根据用户代码构建计算流图;
  2. 根据计算流图拆解出分布式任务;
  3. 将分布式任务分发到 Executors 中去。

接收到任务之后,Executors 调用内部线程池,结合事先分配好的数据分片,并发地执行任务代码。对于一个完整的 RDD,每个 Executors 负责处理这个 RDD 的一个数据分片子集。这就好比是,对于工地上所有的砖头,甲、乙、丙三个工人分别认领其中的三分之一,然后拿来分别构筑东、西、北三面高墙。

spark-shell 执行过程解析

spark-shell --master local[*]

这行代码的含义有两层。第一层含义是部署模式,其中 local 关键字表示部署模式为 Local,也就是本地部署;第二层含义是部署规模,也就是方括号里面的数字,它表示的是在本地部署中需要启动多少个 Executors,星号则意味着这个数量与机器中可用 CPU 的个数相一致。

也就是说,假设你的笔记本电脑有 4 个 CPU,那么当你在命令行敲入 spark-shell 的时候,Spark 会在后台启动 1 个 Driver 进程和 3 个 Executors 进程。

首先,Driver 通过 take 这个 Action 算子,来触发执行先前构建好的计算流图。沿着计算流图的执行方向,也就是图中从上到下的方向,Driver 以 Shuffle 为边界创建、分发分布式任务。

Shuffle 的本意是扑克牌中的“洗牌”,在大数据领域的引申义,表示的是集群范围内跨进程、跨节点的数据交换。在 reduceByKey 算子之前,同一个单词,比如“spark”,可能散落在不用的 Executors 进程,比如图中的 Executor-0、Executor-1 和 Executor-2。换句话说,这些 Executors 处理的数据分片中,都包含单词“spark”。那么,要完成对“spark”的计数,我们需要把所有“spark”分发到同一个 Executor 进程,才能完成计算。而这个把原本散落在不同 Executors 的单词,分发到同一个 Executor 的过程,就是 Shuffle。

对于 reduceByKey 之前的所有操作,也就是 textFile、flatMap、filter、map 等,Driver 会把它们“捏合”成一份任务,然后一次性地把这份任务打包、分发给每一个 Executors。三个 Executors 接收到任务之后,先是对任务进行解析,把任务拆解成 textFile、flatMap、filter、map 这 4 个步骤,然后分别对自己负责的数据分片进行处理。

数据交换之后,所有相同的单词都分发到了相同的 Executors 上去,这个时候,各个 Executors 拿到 reduceByKey 的任务,只需要各自独立地去完成统计计数即可。完成计数之后,Executors 会把最终的计算结果统一返回给 Driver。

  • 26
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

fanghailiang2016

扔个包子砸我一下吧~

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值