Spark科普
- 定义
- 概念
- RDD
- 作业(Job),阶段(stages), 任务
- 应用上下文ApplicationContext
- 转换和动作
- 缓存
- 运行作业机制
- 执行器和任务管理器
- 运行在Yarn上的Spark
定义
Spark是用于大规模数据集群计算的矿建。它可以在YARN上处理HDFS的数据集,但是它并没有使用MapReduce作为它的分布式计算框架,而是自己实现。这样做的好处是提升了数据处理的效率,因为MapReduce的数据集每次都要从磁盘上加载,而Spark可以将作业之间的大规模数据集记录在内存中。这种改进使得Spark在性能上超过了等效的MapReduce工作流。
Spark主要包括用于处理机器学习的MLLib,图算法GraphX,流式计算Spark Streaming,SQL查询 Spark SQL。
概念
RDD
RDD-Resilient Distributed Dataset(弹性分布式数据集),它表示跨多个机器分区存储的一个只读的数据集合(例如从HDFS中加载文件然后存储到分布式集群中的缓存中,包括经过计算得到的中间结果)。Spark应用一般就是要加载一个或多个RDD然后进行一系列转换(map,filter)得到一组目标RDD,然后对这组目标RDD执行一个动作,例如计算出结果或者写入持久存储器。其中“”“弹性”体现在Spark可以重新安排计算来自动重建丢失的分区(计算之后的中间结果,存在内存中的RDD)。
RDD的数据可以通过三种方式获取:
1 来自一个内存中的对象集合。适合少量数据,例如直接在代码中初始化数据集。
2 使用外部存储数据(HDFS)。
3 对现有RDD进行转换。
作业(Job),阶段(stages), 任务
Spark中的Job比MapReduce中的Job更宽泛,因为Spark中的作业是由任意多的阶段(stages)组成的有向无环图(DAG),其中每个阶段(stages)相当于一个map或reduce阶段。
阶段又被Spark运行环境拆分为多个任务(task),任务并行在分布于集群中的RDD分区上,就像MapReduce中的任务一样。
应用上下文ApplicationContext
Spark应用始终运行在应用上下文中,应用上下文提供了RDD分组以及共享变量。一个应用可以串行或并行的运行多个作业,并未这些作业提供访问由同一个应用先前作业所缓存的RDD的机制。
转换和动作
Spark为RDD提供了两大类操作-转换和动作,转换是从现有RDD生成新的RDD,而动作则触发对RDD的计算并对计算结果进行某种操作:返回给用户或者保存报外部存储器中。
区分是转换还是动作可以通过函数返回类型判断,如果返回的是一个RDD那么就是一个转换操作。
缓存
Spark之所以比MapReduce作业高效,一个重要的原因是Spark作业的中间结果可以缓存到集群的内存中。例如:
tuples是一个经过预处理的RDD
tuples.cache()
调用cache()之后,RDD并不会立即被缓存,而是被打上一个标记,表示该RDD在作业运行时应该被缓存。
例如我们现在运行一个作业:
tuples.reduceByKey((a, b) => Math.max(a, b)).foreach(println(_))
==log:
INFO BlockManagerInfo: Added rdd_4_0 in memory on 192.168.1.90:64640
INFO BlockManagerInfo: Added rdd_4_1 in memory on 192.168.1.90:64640
通过日志可以看出RDD已经被写入缓存,编号是4,它有两个分区分别是0和1。
如果我们对tuples运行另外一个作业:
tuples.reduceByKey((a, b) => Math.min(a, b)).foreach(println(_))
==log:
INFO BlockManagerInfo: Found block rdd_4_0 locally
INFO BlockManagerInfo: Found block rdd_4_1 locally
从日志可以看出RDD从内存中加载。
另外需要注意:被缓存的RDD只能由同一个应用的作业来使用。如果要在应用之间共享数据,则必须通过saveAs*方法(例如 saveAsTextFile(),saveAsHadoopFile())将其写入外部存储。
运行作业机制
下图为Spark运行作业的过程
Spark分为driver和executor两个实体。driver负责托管应用(SparkContext)并为作业调度任务。executor专属于应用,它在应用运行期间运行,并执行该应用的任务(task,注意,任务->阶段->任务的从属关系)
通常来说,driver作为一个不由集群管理器(cluster manager)管理的客户端来运行。而executor则运行在集群的计算机上。
- 当对RDD执行一个动作(比如count())时,会自动提交一个Spark作业。从内部看,它导致对SparkContext调用runJob()。
- 将调用传递给调度程序(DAGScheduler),负责把作业分为若干阶段,这些阶段将构成一个DAG。每个阶段又包含不同的任务,例如读取HDFS文件的shuffle map任务(会被赋予一个位置偏好-placement preference 用于标记分配到哪台机器执行以实现数据本地化优化),这些阶段所包含的任务会被提交到TaskScheduler。阶段顺序执行。
- 任务调度程序(TaskScheduler),将会根据DAGScheduler分配的位置偏好将任务分配到相应的executor,默认一个任务分配到一个内核。
任务调度程序在为每个executor分配任务时,首先分配的是进程本地任务(process local),再分配节点本地任务(node local),然后分配机架本地任务(rack local),最后分配任意任务或者推测任务(speculative task)。 - 调度程序后端(SchedulerBackend)负责启动分配的任务,SchedulerBackend通过向executor后端发送远程启动后台任务的消息,以告知executor开始运行任务。
当任务执行完成或失败时,executor都会向driver发送状态更新消息,如果失败了,任务调度程序将会在另一个executor上重新提交任务。 - 任务执行的jvm和executor是同一个jvm,所以启动任务不会有额外的进行开销。
执行器和任务管理器
任务管理器(cluster manager)负责管理executor的生命周期
Spark提供了好多种不同特性的集群管理器:
- 本地模式 executor 与 driver在同一个jvm中,用于测试。
- 独立模式 是一种简单的分布式实现,运行了一个master和多个worker,当spark应用启动时,master要求worker代表应用生成多个executor进程。
- mesos Apache Mesos 是一个通用的集群资源管理器,它允许根据组织策略在不同的应用之间细化资源共享。默认情况下(细粒度模式),每个spark任务被当做是一个mesos任务执行。这样做可以更有效的使用集群资源。但是以额外的进行启动开销为代价。在粗粒度模式下,executor在进行中运行任务,因此在spark应用运行期间的集群资源由executor进程来掌管。
- YARN Yarn是Hadoop中的资源管理器, 每个运行的Spark应用对应于一个Yarn应用实例,每个executor在自己的Yarn容器中运行。可以与Hadoop的Kerberos安全机制继承。
运行在Yarn上的Spark
Spark提供了两种方式(主要是driver在哪里运行的区分):
- Yarn客户端模式。driver在客户端运行。
- Yarn集群模式。driver在Yarn的application master集群上运行。
可以看出集群模式下整个Spark应用都在Yarn集群上运行,包括提交作业,拆分作业的driver,而客户端模式只有executor在Yarn集群下管理。
客户端模式适合于有交互式组件的程序,任何调试输出都是立即可见的。
集群模式适用于生产作业,因为整个应用在集群上运行,这样更易于保留日志(包括来自driver的日志),如果application master出现故障,Yarn还可以尝试重新运行该应用。
集群模式下提交任务,需要输入Yarn-cluster的主url:
% spark-submit --master yarn-cluster ...
下图为客户端模式:
下图为集群模式: