从本节开始,我们将从理论转向实践,介绍在Spark运行程序时内部做了哪些事情。这一节将重点关注在cluster模式下,Spark的调度和执行相关的内容。
SparkContext
当我们创建一个Spark应用时,最先要创建的是SparkContext对象。SparkContext对象告诉Spark如何连接到集群上。在Spark shell中,SparkContext会提前为我们创建好。SparkContext存在driver的程序中,并协调集群上所有运行程序的线程的集合。
SparkContext通过和cluster manager的通信进行executors的分配。这里cluster manager是指一个能够给获取集群资源使用情况的外部服务,比如YARN,Mesos等。
当任务在executors上执行时,SparkContext会直接与之通信,直到任务执行完成。
Jobs, Stages, Tasks
这里介绍Spark中三个非常重要的术语:job,stage和task。
- Job:当存在Action类型的算子时,就会产生一个对应的job,它是最高级的抽象;
- Stage:每个Job会根据依赖关系,划分为多个stage,driver会调用job scheduler,将一个job分为若干个stage,一般是将宽依赖作为stage的划分点;
- Task:实际执行的抽象,每个stage会有对应的一系列task,每个task负责一个分区的计算任务,在executor上执行。
例如以下的代码和对应的计算关系图,整体为一个job,job有两个stage,每个stage有多个tasks,每个tasks计算一个分区的数据。
Z = X.map(lambda x: (x % 10, x / 10)
.reduceByKey(lambda x, y: x + y)
.collect()
所有这些机制都存在于SparkContext对象中,它负责生成作业,运行调度程序,和跟踪执行程序等。
stage和task的区别
在重点强调一下stage和task这两个概念的区别:
- Stage:定义在RDD级别,并不是立即执行的。划分Stage的目的是尽可能减少中间数据的物化,在同一个stage的属于窄依赖的多个transformations,例如多个filter算子,是可以在一次执行中完成的。只有是宽依赖的结果才必须要将数据物化。
- Task:定义在特定的分区上的,并且是立即执行的。
其他功能
再来看一下SparkContext还完成了哪些其他功能:
- 跟踪executors是否存活,通过心跳机制发现故障的executors,保证计算过程的高可用性;
- 对于更复杂的应用,做多个并行jobs的调度;
- 在cluster manager允许的情况下,进行动态资源分配,可以提升多个应用共享资源时的资源利用率。
小结
- SparkContext是Spark应用的核心,它允许应用连接到Spark集群,并分配资源和executors等。
- 当调用action算子时,SparkContext产生一个job,并通过job scheduler生成该job的stages。多个连续的窄依赖可以划分在同一个stage中。每个stage生成多个tasks,task分配到executors上执行。
- Driver会和executors通信,获取任务信息和executors存活状态。