Spark入门

(一) Spark简介

Spark 是专门为大数据处理而设计的快速、易用、通用和进行复杂分析的计算框架。
Spark与Hadoop的对比(★★★★★)
Hadoop和spark均是大数据框架,都提供了一些大数据任务的处理工具。但确切讲,他们执行的任务并不相同,彼此不是相互排斥的。虽然在特定的情况下,spark要比Hadoop的计算快很多,但它没有一个分布式的存储系统。而分布式存储系统是大数据处理的基础。因此spark需要一个第三方的分布式存储系统,许多大数据项目都将spark安装在Hadoop上。

spark是在借鉴了mapreduce的基础上,继承其优点,改进其缺点而发展起来的,其真正的优势在于速度。

1,spark把计算的中间结果放到内存,迭代计算效率更高;而mapreduce的中间结果要落地到磁盘,增加了io的开销,影响性能。但是放到内存中的数据容易丢失,这也是spark相对不稳定的原因。
2,spark容错性高,RDD之间的血缘关系可以让它恢复数据时有高的性能。而mapreduce只能重新计算。
3,spark更加通用而全面。不仅可以做离线批处理,还可以做流式处理(sparkstreaming),图计算(GraphX)等。
4,spark框架更加复杂,RDD、RDD之间的依赖、血缘、执行的DAG处理、stage划分等。
运行模式
local:主要用于开发和调试Spark程序
standlone:利用spark自带的资源管理与调度器运行spark集群,采用master/slave结构。可以使用Zookeeper实现高s可用(HA)。
Apache Mesos:程序运行在Mesos资源管理框架基础上,该方式将资源管理交给Mesos,而Spark只负责任务调度和计算。
Hadoop YARN:将资源管理交给YARN,Spark只负责资源调度和计算。
spark组件

image

Spark执行流程:
每个spark应用都由一个驱动器函数(Driver Program)来发起集群中的各种操作。驱动程序包括引用的主函数main,
驱动器负责创建SparkContext,SparkContext可以与不同种类的集群资源管理器(Cluster Manager),例如YARN,Mesos进行通信。获取到集群运行所需的资源后, SparkContext将得到集群中工作节点(Worker Node)上对应的Executor(不同的spark程序有不同的executor,他们之间相互隔离独立,Executor为应用程序提供分布式计算和数据存储功能),之后SparkContext将应用程序代码发送给各个Executor,最后将任务(Task)分配给Executor执行。
总结:

  1. Client向Driver Program提交应用程序,执行main()函数,生成sparkContext。
  2. sparkContext找到资源管理器ClusterManager,即Master,Master找到WorkerNode,启动Driver。sparkContext将应用程序通过master发送给WorkerNode
  3. Driver向Master申请计算资源,将应用转换为RDD Graph。再由DAG Scheduler将RDD Graph转换为基于stage的有向五环图,提交给Task Scheduler。
  4. Task Scheduler将任务分发给具体的Executor,启动相应的Task任务。
  5. 其他组件相互协调,保证应用的顺利执行。

各组件解释

Application:运行在集群上的用户程序,包含Driver Program和多个executor线程组成。
CluseterManager: 资源管理器。 在standalone模式中就是指主节点master,控制整个集群,监控worker;若是在YARN中,就是指ResourceManager。
WorkerNode: 工作节点,运行Application。负责控制计算节点,启动Executor或Driver。在YARN中就是指NoneManager。
DriverProgram: 运行Application的main()方法,生成SparkContext。
Executor: worknode上的进程,运行task并保持数据交互,每个Application有自己的executor。
SparkContext:整个应用的上下文,控制应用的生命周期。
Task: 运行于Executor的应用单元,spark程序最终被优化为划分为多个任务的集合。
Job: 由多个转变构建的并行计算任务,具体为spark中的action操作,一个action就是一个job。
RDD:spark的计算单元,一组RDD可形成执行的有向无环图。
Task Scheduler: 将任务分发给Executor。

(二) Spark的核心RDD

弹性分布式数据集,是spark最基本的数据抽象,代表一个分布式、不可变、分区的、数据可并行化运算的集合。
RDD允许用户将计算集存放在内存而不是磁盘,后续的查询可直接调用,因此极大提高了执行速度。
RDD 的特点
一、 RDD是Spark的核心数据模型,但是个抽象类。全称是Resillient Distribute Dataset。
二、RDD是一种元素集合,包含数据。他是被分区的,分为多个区,每个分区分布在不同的节点上,从而让其中的数据可以并行化执行。
三、RDD通常来说是通过读取HDFS上的文件或者Hive的表来创建。
四、RDD最重要的特性就是有很好的容错性。因为RDD的许多操作会返回新的RDD,这两个RDD就有了依赖关系,当前节点出了问题,可以由前面的节点来恢复,而非重新计算。
五、RDD的数据默认情况下保存在内存中,但是当内存不足时也会保存在磁盘中。  
RDD的创建方式
spark编程的第一步就是创建一个初始的RDD。该RDD就包含了spark应用程序的输入数据源。然后通过其他的操作来对这个RDD进行转换得到其他RDD。  
常见有三种创建RDD的方式:
1,使用程序中的集合创建:
    val arr = Array(1,2,3,4)
    val rdd = sc.parallelize(arr)
2,读取外部文件系统:
    val rdd = sc.textfile("hdfs path / hbase / hive ...")
3,由RDD的transformation操作转换而。
RDD的操作
RDD支持两类操作: Transformation和action。
  1. transformation
    这种操作会针对已有的RDD创建新的RDD。其具有懒惰加载的特性,即transformation不会触发spark的程序执行,只是记录RDD的操作。只有遇到action操作时才会将之前所有的数据加载执行。

常用transformation操作:

transformation算子解释
map(func)将RDD传入的每个元素执行func函数,由返回的值构成新RDD
filter(func)对每个元素判断,若结果为True,则保留
flatMap(func)与map类似。不同的是一个元素它可以映射为一个或多个值
union(dataSet)与传入的RDD合并后返回
groupByKey([numsTasks])在一个<k,v>的RDD上调用,返回一个<k,Iterator[V]>的RDD
reduceByKey(func,[numTasks])在一个<k,v>的RDD上调用,返回一个<k,V>的RDD。使用指定的reduce函数将key的值聚集到一起,reduce的数量可以通过第二个参数指定
sortByKey对每个相同key的value进行排序
join(dataSet,[numTasks])对两个包含<k,v>的RDD进行join操作,把具有相同key的连接在一起,返回<k,(v1,v2,…,vn)>
repartition(numPartions)重新分区,会经过shuffle阶段。传入新的分区数。
coalesce(numpartitions)重新分区
  1. action
    对RDD进行最后的操作,比如遍历,reduce,保存到本地等,并且可以返回结果给Driver。action会触发一个spark Job的运行,而这个Job会出触发此action之前的所有transformation操作。

常用action操作

action算子解释
reduce(func)将RDD所有元素进行聚合。第一个与第二个的值,与下一个,指导最后
collect将RDD的元素以数组的形式返回(不建议使用)
count()RDD的元素个数
take(n)获取RDD的前n个元素
saveAsTextFile(path)将RDD保存到HDFS或其他支持的文件系统中。调用toString()方法,元素会被转为字符串
countByKey()针对<k,v>类型的RDD,返回一个(key,int)的map,表示每个k所含元素的个数
lookup(elem)针对<k,v>形式的RDD,返回k的值与传入elem相同的元素集合Seq

重要的几个函数对比

  • map:会将每一个输入映射为一个新对象。
  • flatMap:对每个元素进行映射,然后再扁平化处理。
  • reduce: 对同种类型的RDD进行操作,返回一个同样类型的新元素。要求传入两个参数,返回一个新值。新值在与下一个元素传入函数。直到最后一个元素。
    example:
val arr = sc.parallelize(Array(("A",1), ("B",2),("C",3)))  
arr.flatmap(x=>(x._1 + x._2)).foreach(println)
结果:
    A,1,B,2,C,3,
arr.map(x=>(x._1+x._2)).foreach(println)
结果:
    A1
    B2
    C3
其实flatmap可以理解为,如果自定义的操作返回的是一个可迭代对象,那么它就会自动将返回
结果“平整”到一起。如果不是可迭代的,就会报错。

arr1 = sc.parallelize([1,2,3,4])
arr1.reduce((x,y) => x+y)
结果:
    10
  • groupByKey:将键相同的所有键值对分组到一个集合,如(a,1),(a,2),(b,1),(b3)分组后的结果是:(((a,1),(a,2)),((b,1),(b,3)))。这是一个相对而言比较昂贵的方法,比较消耗资源,如果你是相对分组后的结果做一些统计或其他的指标计算,建议使用reduceByKey或aggregateByKey。groupByKey会把所有键值对集合加载到内存中计算,如果某个键对应的值太多,可能会导致内存溢出。

  • reduceByKey:可以理解为groupByKey + reduce 操作。效率上要比groupByKey要效率高,这是由于在map端自带combiner操作。
    二者做一个对比:
    ①返回值不同:reduceByKey返回的是
    ②作用不同:reduceByKey主要用来做聚合等;groupByKey主要做分组,分组后可以再做聚合。
    ③map端中间结果对键值对的处理不同。groupByKey自带combiner操作。

  • repartition与coalesce:
    在spark中,RDD是分区的。有时候需要重新设置分区数,比如说RDD分区太多,但是RDD数据量太小,造成浪费资源;又或者需要增加分区,减小每个task的计算量。这时就可以用到两个分区函数repartition与coalesce。
    这两个都是对RDD的分区重新划分,repartition只是coalesce接口中shuffle为true的简易实现,现在假设RDD有N个分区,需重新划分为M个分区:
    1)若N<M,这是一个宽依赖,coalesce必须将shuffle设置为true,或者直接用repartition。若shuffle还是false的话,分区数是不会变的。
    2)若N>M,且N与M相差不是太大,那么可以将N中的几个分区合并成一个分区,使用coalesce算子,shuffle设置为false。
    3)若N<M,二者相差非常大,比如M=1,这时所有的计算同处一个stage,并行度不够,影响性能。这时可以将coalsesce设置为true,可以有一个更好的并行度。

RDD的依赖
spark中RDD可以高效的执行与DAG图有很大的关系,在DAG调度中需要对计算
过程划分stage。而划分的依据就是RDD之间的依赖关系。
宽依赖和窄依赖:
  • 窄依赖是指父RDD的一个分区只被子RDD的一个分区使用;而子RDD的一个分区通常对应多个父RDD分区。
    
  • 宽依赖是指父RDD的一个分区可以被多个子RDD所用;而子RDD的每个分区对应于所有的父RDD分区。
    

image

相比于宽依赖,窄依赖对优化更有利:
1,宽依赖对应着shuffle操作,需要在执行过程中把父RDD的一个分区传入到不同的子RDD的分区中,中间会涉及到多个节点之间的数据传输; 而窄依赖每个父节点的RDD的分区只会对应一个子RDD分区,这通常在一个节点内完成。
2,当RDD分区数据丢失时,spark会对数据进行 重算:
    1,对于窄依赖,由于父RDD每个分区对应一个子RDD的分区,这样只需要计算与这个字RDD分区对应的那些父RDD分区即可。这个重算对数据的利用率是100%的。
    2,对于宽依赖,重算的父RDD的分区实际上对应多个子RDD的分区,计算的结果有一部分是用来恢复数据,但还有一部分对应于子RDD中未发生数据丢失的分区,这就造成了计算的浪费。极端情况下需要把父RDD所有的分区都计算一次。如下图所示:
image

窄依赖的算子:map, union, filter, join(父RDD是hash-partitioned), mapPartitions, mapValues
宽依赖的算子:groupByKey, join(父RDD不是hash-partitioned), partitionBy

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值