Spark原理(二)

来源:互联网 发布:mac os 国外软件推荐 编辑:程序博客网 时间:2024/05/21 10:57

Point 1:Spark工作原理图
这里写图片描述
这里写图片描述
这里写图片描述

Point 2: Resilient Distributed Dataset(RDD)弹性分布数据集

RDD是Spark的最基本抽象,是对分布式内存的抽象使用,实现了以操作本地集合的方式来操作分布式数据集的抽象实现。RDD是Spark最核心的东西,它表示以被分区,不可改变的并能够被并行操作的数据集合,不同的数据集格式对应不同的RDD实现。RDD必须是可序列化的。RDD可以cache到内存中,每次对RDD数据集的操作之后的结果,都可以存到内存中,下一个操作可以直接从内存中输入,省去了MapReduce大量的磁盘IO操作。这对于迭代运算比较常见的机器学习算法,交互式数据挖掘来说,效率提升比较大。

Point 3:RDD的特点
● 它是集群节点上的不可改变的、已分区的集合对象;
● 通过并行转换的方式来创建如(map、filter、join等)。
● 失败自动重建;
● 可以控制存储级别(内存、磁盘等)来进行重用;
● 必须是可序列化的;
● 是静态类型的

Point 4:RDD的好处
● RDD只能从持久存储或通过Transformations操作产生,相比于分布式共享内存(DSM)可以更高效实现容错,对于丢失部分数据分区只需要根据它的lineage就可重新计算出来,而不需要做特定的checkpoint。
● RDD的不变性,可以实现类Hadoop MapReduce的推测式执行;
● RDD的数据分区特性,可以通过数据的本地性来提高性能,这与Hadoop MapReduce是一样的;
● RDD都是可序列化的,在内存不足时可自动降级为磁盘存储,把RDD存储于磁盘上,这时性能有大的下降但不会差于现在的MapReduce;

Point 5:RDD的存储与分区
● 用户可以选择不同的存储级别存储RDD以便重用;
● 当前RDD默认是存储于内存,但当内存不足时,RDD会spill到disk;
● RDD在需要进行分区把数据分布于集群中时会根据每条记录key进行分区(如Hash分区),以保证两个数据集在Join时能高效;

Point 6:RDD的内部表示
在RDD的内部实现中每个RDD都可以使用5个方面的特性来表示:
● 分区列表(数据块列表)
● 计算每个分片的函数(根据父RDD计算此RDD)
● 对父RDD的依赖列表
● 对key-value RDD的partitioner【可选】
● 每个数据分片的预定义地址列表(如HDFS上的数据块的地址)【可选】

Point 7:RDD的存储级别
RDD根据useDisk、useMemory、deserialized、replication四个参数的组合提供了11种存储级别:
这里写图片描述
RDD定义了各种操作,不同类型的数据由不同的RDD类抽象表示,不同的操作也由RDD进行抽象实现。

Point 8:RDD的生成
RDD有两种创建方式:①从Hadoop文件系统(或者与Hadoop兼容的其它存储系统)输入创建;②从父RDD转换得到新RDD。
下面来看一从Hadoop文件系统生成RDD的方式,例如:val file = spark.textFile(“hdfs://…”),file变量就是RDD(实际是HadoopRDD实例),生成的它的核心代码如下:

  1. //SparkContext根据文件/目录及可选的分片数创建RDD, 我们可以看到Spark与Hadoop MapReduce很像    2. //需要InputFormat, Key、Value的类型,其实Spark使用的Hadoop的InputFormat, Writable类型。    3. Def textFile(path:String,minSplits:Int=defaultMinSplits):RDD[String]={    4.         hadoopFile(path,classOf[TextInputFormat],classOf[LongWritable],    5.         classOf[Text],minSplits).map(pair=>pair._2.toString)    6. }    7. //根据Hadoop配置,及InputFormat等创建HadoopRDD      8.  New HadoopRDD(this,conf,inputFormatClass,keyClass,valueClass,minSplits)      对RDD进行计算时,RDD从HDFS读取数据时与Hadoop MapReduce几乎一样的:  1. // 根据hadoop配置和分片从InputFormat中获取RecordReader进行数据的读取。       2. reader = fmt.getRecordReader(split.inputSplit.value, conf, Reporter.NULL)      3. val key: K = reader.createKey()      4. val value: V = reader.createValue()      5. //使用Hadoop MapReduce的RecordReader读取数据,每个Key、Value对以元组返回。      6. override def getNext() = {      7.     try {      8.         finished = !reader.next(key, value)      9.     } catch {      10.         case eof: EOFException =>      11.         finished = true      12.     }      13.    (key, value)      14. }   

Point 9:RDD的转换与操作
这里写图片描述
● 对于RDD可以有两种计算方式:转换(返回值还是一个RDD)与操作(返回值不是一个RDD);
● 转换(Transformations)(如:map、Filter、groupby、join等),Transformations操作是Lazy的,也就是说从一个RDD转换生成另一个RDD的操作不是马上执行,spark在遇到Transformations操作时只会记录需要这样的操作,并不会去执行,需要等到有Actions操作的时候才会真正启动计算过程进行计算。
● 操作(Actions)(如:count、collect、save等),Actions操作会返回结果或把RDD数据写到存储系统中。Actions是触发Spark启动计划的动因。
● 下面使用一个例子来示例说明Transformations与Actions在Spark的使用

  1. Val sc=new SparkContext(master,"Example",System.getenv("SPARK_HOME"),Seq(System.getenv("SPARK_TEST_JAR")))      2. val rdd_A=sc.textFile(hdfs://.....)      3.     4. val rdd_B=rdd_A.flatMap((line=>line.split("\\s+"))).map(word=>(word,1))      5. val rdd_C=sc.textFile(hdfs://.....)      6. val rdd_D=rdd_C.map(line=>(line.substring(10),1))      7. val rdd_E=rdd_D.reduceByKey((a,b)=>a+b)      8.        9. val rdd_F=rdd_B.jion(rdd_E)      10. rdd_F.saveAsSequenceFile(hdfs://....)  

Point 10:血统(Lingage)
● 利用内存加快数据加载,在众多的其他的In-Memory类数据库或Cache类系统中也有实现,Spark的主要区别在于它处理分布式运算环境下的数据容错性(节点时效/数据丢失)问题时采用的方案。为了保证RDD中数据的鲁棒性,RDD数据集通过所谓的血统关系(Lineage)记住了它是如何从其他RDD中演变过来的。相比其他系统的细粒度的内存数据更新级别的备份或者LOG机制,RDD的Lineage记录的是粗颗粒度的特定数据转换(Transformations)操作行为。当这个RDD的部分分区数据丢失时,它可以通过Lineage获取足够的信息来重新运算和恢复丢失的数据分区。这种粗颗粒的数据模型,限制了Spark的运用场合,但同时相比细颗粒度的数据模型,也带来了性能的提升。
● RDD在Lineage依赖方面分为两种Narrow Dependencies与Wide Dependencies用来解决数据容错的高效性。Narrow Dependencies是指父RDD的每一个分区最多被一个子RDD的分区所用,表现为一个父RDD的分区对应于一个子RDD的分区或多个父RDD的分区对应于一个子RDD的分区,也就是说一个父RDD的一个分区不可能对应一个子RDD的多个分区。Wide Dependencies是指子RDD的分区依赖于父RDD的多个分区或所有分区,也就是说存在一个父RDD的一个分区对应一个子RDD的多个分区。对与Wide Dependencies,这种计算的输入和输出在不同的节点上,lineage方法对与输入节点完好,而输出节点宕机时,通过重新计算,这种情况下,这种方法容错是有效的,否则无效,因为无法重试,需要向上其祖先追溯看是否可以重试(这就是lineage,血统的意思),Narrow Dependencies对于数据的重算开销要远小于Wide Dependencies的数据重算开销。

Point 11:容错
在RDD计算,通过checkpint进行容错,做checkpoint有两种方式,一个是checkpoint data,一个是logging the updates。用户可以控制采用哪种方式来实现容错,默认是logging the updates方式,通过记录跟踪所有生成RDD的转换(transformations)也就是记录每个RDD的lineage(血统)来重新计算生成丢失的分区数据。

原创粉丝点击