Spark核心-RDD

什么是RDD?

RDD是Spark中的数据抽象,全称弹性分布式数据集(Resilient Distributed Datasets)。RDD可以理解为将一个大的数据集合以分布式的形式保存在集群服务器的内存中。RDD是一个容错的、并行的数据结构,可以让用户显式地将数据存储到磁盘和内存中,并能控制数据的分区。

RDD是Spark的核心,也是整个Spark的架构基础。

RDD的特点:

  • 不可变性
    RDD是一种不可变的数据结构。一旦创建,就不能修改。
  • 分片
    RDD表示的是一组数据的分区。这些分区分布在集群的多个节点上。RDD中存储了分区和数据物理分区之间关系的映射。
  • 容错性
    RDD可以自动处理节点出现故障的情况。当某个节点出现故障时,该节点上存储的数据将无法被访问。Spark会在其他节点上重建丢失的RDD分区数据。RDD中存储了血统信息,通过血统信息,Spark可以恢复RDD的部分信息,当节点出现故障的时候,可以基于血统恢复整个RDD。
  • 内存计算
    RDD类提供了一套支持内存计算的API。RDD可以在内存中缓存或长期驻留,从而提升了对RDD操作的效率。

RDD的5个主要属性:

  //只计算一次  
  protected def getPartitions: Array[Partition]  
  //对一个分片进行计算,得出一个可遍历的结果
  def compute(split: Partition, context: TaskContext): Iterator[T]
  //只计算一次,计算RDD对父RDD的依赖
  protected def getDependencies: Seq[Dependency[_]] = deps
  //可选的,分区的方法,针对第4点,类似于mapreduce当中的Paritioner接口,控制key分到哪个reduce
  @transient val partitioner: Option[Partitioner] = None
  //可选的,指定优先位置,输入参数是split分片,输出结果是一组优先的节点位置
  protected def getPreferredLocations(split: Partition): Seq[String] = Nil
  1. 一组分片(Partition)。对于RDD来说,每个分片都会被一个计算任务处理,并决定并行计算的粒度。
  2. 一个计算每个分区的函数。Spark中的RDD的计算是以分片为单位的,每个RDD都会实现compute函数来完成计算任务。
  3. RDD之间的依赖关系。RDD的每次转换都会生成一个新的RDD,所以RDD之间就会形成类似于刘水线一样的前后依赖关系。在部分分区数据丢失时,Spark可以通过这个依赖关系重新计算丢失的分区数据,这样就不用对RDD的所有分区进行重新计算。源RDD没有依赖,通过依赖关系描述血统(lineage)。
  4. 一个Partitioner(RDD的分片函数)。当前Spark中实现了两种类型的分片函数,一个是基于哈希的HashPartitioner,另外一个是基于范围的RangePartitioner。只有对于key-value的RDD,才会有Partitioner,非key-value的RDD的Partitioner的值是None。Partitioner函数决定了RDD本身的分片数量,也决定了parent RDD Shuffle输出时的分片数量。
  5. 一个存储每个Partition的优先位置的列表。对于一个HDFS文件来说,这个列表保存的就是每个partition所在的块的位置。Spark在进行任务调度的时候,会尽可能地将计算任务分配到其所要处理数据块的存储位置(移动数据不如移动计算)。

RDD的创建

可以通过两种方式创建RDD:

  1. 由并行化集合创建
  2. 由外部存储系统的数据集创建,包括本地的文件系统、Hadoop文件系统HDFS或从Hadoop接口API创建
// 由并行化集合创建
val list = (1 t0 100).toList
val rdd = sc.parallelize(list)

// 由外部存储系统创建
val rdd = sc.textFile("/path/data")
val rdd = sc.wholeTextFiles("/path/data")

RDD的操作

image

RDD转换操作

转换操作指的是在原RDD实例上进行计算,然后创建一个新的RDD实例。

RDD中的所有的转换操作都是惰性的,在执行RDD的转换操作的时候,并不会直接计算结果,而是记住这些应用到基础数据集上的转换动作,只有行动操作时,这些转换才会真正的去执行。这样设计的好处是更加有效率的运行。

惰性求值的好处:
Spark使用惰性求值,这样就可以把一些操作合并到一起来减少计算数据的步骤。

RDD行动操作

行动操作指的是向驱动器程序返回结果或把结果写入外部系统的操作。

Spark在调用RDD的行动操作的时候,会触发Spark中的连锁反应。当调用的行动操作的时候,Spark会尝试创建作为调用者的RDD。如果这个RDD是从文件中创建的,那么Spark会在worker节点上读取文件至内存中。如果这个RDD是通过其他RDD的转换得到的,Spark会尝试创建其父RDD。这个过程会一直持续下去,直到Spark找到根RDD。然后Spark就会真正执行这些生成RDD所必须的转换计算。最后完成行动操作,将结果返回给驱动程序或者写入外部存储。

img

RDD的缓存

Spark速度非常快的原因之一,就是在不同操作中在内存中持久化一个数据集。当持久化一个RDD后,每一个节点都将把计算的分片结果保存在内存中,并在对此数据集进行的其他动作中重用。这使得后续的动作变得更加迅速。缓存是Spark构建迭代算法和快速交互式查询的关键。所以我们在开发过程中,对经常使用的RDD要进行缓存操作,以提升程序运行效率。

RDD缓存的方法

RDD类提供了两种缓存方法:

rdd.cache()
rdd.persist()

cache方法其实是将RDD存储在集群中Worker的内存中。

persist是一个通用的cache方法。它可以将RDD存储在内存中或硬盘上或者二者皆有。

存储级别 描述
MEMORY_ONLY 默认选项,RDD的(分区)数据直接以Java对象的形式存储于JVM的内存中,如果内存空间不足,某些分区的数据将不会被缓存,需要在使用的时候重新计算。
MEMORY_AND_DISK RDD的数据直接以Java对象的形式存储于JVM的内存中,如果内存空间不中,某些分区的数据会被存储至磁盘,使用的时候从磁盘读取。
MEMORY_ONLY_SER (Java and Scala) RDD的数据(Java对象)序列化之后存储于JVM的内存中(一个分区的数据为内存中的一个字节数组),相比于MEMORY_ONLY能够有效节约内存空间(特别是使用一个快速序列化工具的情况下),但读取数据时需要更多的CPU开销;如果内存空间不足,处理方式与MEMORY_ONLY相同。
MEMORY_AND_DISK_SER (Java and Scala) 相比于MEMORY_ONLY_SER,在内存空间不足的情况下,将序列化之后的数据存储于磁盘。 DISK_ONLY 仅仅使用磁盘存储RDD的数据(未经序列化)。
MEMORY_ONLY_2, MEMORY_AND_DISK_2, etc. 以MEMORY_ONLY_2为例,MEMORY_ONLY_2相比于MEMORY_ONLY存储数据的方式是相同的,不同的是会将数据备份到集群中两个不同的节点,其余情况类似。
OFF_HEAP (experimental) 与MEMORY_ONLY_SER类似,但是存储在非堆的内存中,需要开启非堆内存。

缓存的容错

缓存是有可能丢失(如机器宕机),或者存储于内存的数据由于内存不足而被删除。RDD的缓存的容错机制保证了即使缓存丢失也能保证计算的正确执行。通过基于RDD的一系列的转换,丢失的数据会被重新计算。因为RDD的各个Partition是相对独立的,所以在重新计算的时候只需要计算丢失部分Partition即可,不需要重新计算全部的Partition。因此,在一个缓存RDD的节点出现故障的时候,Spark会在另外的节点上自动重新创建出现故障的节点中存储的分区。

RDD的检查点

RDD的缓存能够在第一次计算完成后,将计算结果保存到内存、本地文件系统或者Tachyon中。通过缓存,Spark避免了RDD上的重复计算,能够极大地提升计算速度。但是,如果缓存丢失了,则需要重新计算。如果计算特别复杂或者计算特别耗时,那么缓存丢失对于整个Job的影响是不容忽视的。为了避免缓存丢失重新计算带来的开销,所以Spark引入了检查点(checkpoint)机制。

缓存是在计算结束后,直接将计算结果通过用户定义的存储级别写入不同的介质。而检查点不同,它是在计算完成后,重新建立一个Job来计算。所以为了避免重复计算,推荐先将RDD缓存,这样在进行检查点操作时就可以快速完成。

RDD依赖

Spark会根据用户提交的计算逻辑中的RDD的转换和动作来生动RDD之间的依赖关系,同时这个计算链也就生成了逻辑上的DAG。

RDD之间的依赖关系包括:

  1. 一个RDD是从哪些RDD转换而来,即RDD的parent RDD(s)是什么
  2. 依赖于parent RDD(s)的哪些分区

Spark中的依赖关系主要体现为两种形式:

  • 窄依赖:是指父RDD的每一个分区最多被一个子RDD的分区使用。
  • 宽依赖:是指子RDD的每个分区都依赖于所有父RDD的所有所有分区或多个分区。
RDD的依赖关系
  1. 窄依赖允许在单个集群节点上流水线式执行,这个节点可以计算所有父级分区。
  2. 在窄依赖中,节点失败后的恢复更加高效。因为只有丢失的父级分区需要重新计算,并且这些丢失的父级分区可以并行地在不同节点上重新计算。但是在宽依赖的继承关系中,单个失败的节点可能导致一个RDD的所有先祖RDD中的一些分区丢失,导致计算的重新执行。
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342