从WordCount看Spark大数据处理的核心机制(2)中我们看到Spark为了支持迭代和交互式数据挖掘,而明确提出了内存中可重用的数据集RDD。RDD的只读特性,再加上粗粒度转换操作形成的Lineage,形成了它独立的高效容错机制。

RDD的粗粒度的转换是否有足够的表达能力,来支持多种多样的应用需求呢?先看看RDD究竟有哪些API,然后看它们如何模拟Google经典的MapReduce和图数据处理框架Pregel。

RDD的API

转换

  1. def map[U](f: T => U): RDD[U]

将RDD[T]经过f转换成RDD[U],T和U一一映射,两个RDD元素个数相等

  1. def flatMap[U](f: T => TraversableOnce[U]): RDD[U]

将RDD[T]经过f闭包转换成RDD[U],一个T可以映射成0到多个U,两个RDD元素通常不等

  1. def mapPartitions[U: ClassTag](
  2. f: Iterator[T] => Iterator[U]): RDD[U]

mapPartitions是partition级的转换,多元素到多元素或单元素的转换。

还记得从WordCount看Spark大数据处理的核心机制(1)中我们扒开的countByValue函数吗?它就是通过mapPartitions来统计每个partition上所有单词的计数。

  1. def union(other: RDD[T]): RDD[T]

将两个RDD[T]合并成一个RDD[T]。

可能一开始会觉得union操作会耗时较大,实际上这个操作非常廉价。RDD的元信息中包含了Partition/Lineage等信息,union只是合并元信息,而并不涉及具体的数据,so easy。

  1. def distinct(): RDD[T]

将原RDD[T]转换成新的RDD[T],但每个元素只出现一次。

distinct从业务意义上很容易理解,但消耗却不少,需要通过网络交换各个Partition的数据,小伙伴们要注意了。

以下所有的转换都仅针对RDD[(K, V)]有效,是通过把RDD[(K, V)]隐式转换成PairRDDFunctions[K, V]获得的。使用前一定要导入SparkContext内的隐式转化函数,如下:

  1. import org.apache.spark.SparkContext._

不然找不到下面的函数,不要说一码不负责乱说哈。

隐式转换是Scala带来的好东西,类似于C#或Ruby中可以把类打开的功能,实在是写出优雅代码不可多得的工具。不清楚的小伙伴记得一定要GFSOSO哈。

  1. def groupByKey(): RDD[(K, Iterable[V])]

将RDD[(K, V)]中所有(K, V)键值对按K进行分组,每组一个元素,形成新的RDD[(K, Iterable[V])]。

  1. def reduceByKey(func: (V, V) => V): RDD[(K, V)]

将RDD[(K, V)]中所有(K, V)键值对按K进行分组归并,最终每个K只有一个(K, V)与之对应。

  1. def join[W](other: RDD[(K, W)]): RDD[(K, (V, W))]

将RDD[(K, V)]与RDD[(K, W)做join操作,形成新的RDD[(K, (V, W)),最终形成的RDD中只有两个RDD中共有的K。

  1. def cogroup[W](other: RDD[(K, W)]): RDD[(K, (Iterable[V], Iterable[W]))]

将RDD[(K, V)]与RDD[(K, W)一起分组。

值得注意的是:结果里面包含两个RDD中所有的K,也就是说Iterable[V]和Iterable[W]中可能某个为空。

  1. def mapValues[U](f: V => U): RDD[(K, U)]

仅对(K, V)中的V进行转换,转换后和原先的K一起形成新的(K, U)键值对,通常和groupByKey一起使用。

  1. def partitionBy(partitioner: Partitioner): RDD[(K, V)]

将RDD[(K, V)]按照K进行重新分区。

重新分区对每个分区数据非常少的情况很有帮助。减少分区,任务数量也会随着分区的减少而减少,降低大量任务调度的开销。

所有转换返回的一定是RDD。

动作

  1. def count(): Long

统计RDD[T]中元素T的个数。

  1. def reduce(f: (T, T) => T): T

对RDD[T]中的元素进行两两合并,最终合并成一个值。

比如对RDD[Int]中的所有元素求和:ints.reduce((i1, i2) => i1 + i2)

  1. def collect(): Array[T]

将RDD[T]中所有元素从Slave上收集到Driver上。

该方法应该只应用在元素较少的RDD上,否则Driver一定OutOfMemory。

  1. def saveAsTextFile(path: String): Unit

把RDD[T]中的所有元素保存到磁盘,通常是保存到分布式文件系统。

注意path是个目录,RDD中的每个元素对应了目录下的一个文件。保存后形成:/path/part-00000,

/path/part-00001等文件。最大的好处就是解决了通过collect到Driver再保存到磁盘的问题。

常用的RDD API就上面这些,更多请参考Spark官方文档。

所有动作返回的一定不是RDD。

转换不会加载数据,仅记录Lineage而已,而动作会触发数据的加载,并根据Lineage完成所有的转换,是延迟计算,极大地提升效率。

RDD对MapReduce的模拟

来看这篇文章的小伙伴们应该都清楚MapReduce模型了,它很容易使用RDD进行描述。假设有一个输入数据集(其元素类型为T),和两个函数myMap: T => List[(Ki, Vi)] 和 myReduce: (Ki; List[Vi]) ) List[R],RDD API模拟代码如下:

  1. data.flatMap(myMap)
  2. .groupByKey()
  3. .map((k, vs) => myReduce(k, vs))

如果任务包含combiner,则相应的代码为:

  1. data.flatMap(myMap)
  2. .reduceByKey(myCombiner)
  3. .map((k, v) => myReduce(k, v))

RDD对Pregel图计算的模拟

Pregel是面向图算法的基于BSP范式的编程模型。程序由一系列超步(Superstep)协调迭代运行。在每个超步中,各个顶点执行用户函数,并更新相应的顶点状态,变异图拓扑,然后向下一个超步的顶点集发送消息。这种模型能够描述很多图算法,包括最短路径,双边匹配和PageRank等,我们以PageRank为例来说明。

PageRank可是搜索引擎的基础,经典的大数据算法,还不知道的小伙伴请自行GFSOSO哈。

当前PageRank记为r,顶点表示状态。在每个超步中,各个顶点向其所有邻居发送贡献值r/n,这里n是邻居的数目。下一个超步开始时,每个顶点将其分值(rank)更新为 α/N + (1 - α) * Σci,这里的求和是各个顶点收到的所有贡献值的和,N是顶点的总数。

Pregel的通信模式可以用RDD来描述,主要思想是:将每个超步中的顶点状态和要发送的消息存储为RDD,然后根据顶点ID分组,进行Shuffle通信(即cogroup操作)。然后对每个顶点ID上的状态和消息应用用户函数(即mapValues操作),产生一个新的RDD,即(VertexID, (NewState, OutgoingMessages))。然后执行map操作分离出下一次迭代的顶点状态和消息(即mapValues和flatMap操作)。代码如下:

  1. val vertices = // RDD of (ID, Vertice) pairs
  2. val incomingMessages = // RDD of (ID, Message) pairs
  3. val grouped = vertices.cogroup(incomingMessages)
  4. val newData = grouped.mapValues {
  5. (vert, incomingMsgs) => spreadRank(vert, incomingMsgs)
  6. // returns (newState, outgoingMsgs)
  7. }.cache()
  8. val newVerts = newData.mapValues((v,ms) => v)
  9. val newMsgs = newData.flatMap((id,(v,ms)) => ms)
  10. def spreadRank(...): ... = {
  11. // spread the incoming rank to outgoing rank
  12. }

需要注意的是,这种实现方法中,RDD grouped,newData和newVerts的分区方法与输入RDD vertices一样。所以,顶点状态一直存在于它们开始执行的机器上,这跟原Pregel一样,这样就减少了通信成本。因为cogroup和mapValues保持了与输入RDD相同的分区方法,所以分区是自动进行的。

如果觉得上面这一段有难度,请在微信公众号上联系一码。

经过四篇文章,Spark基础知识还剩下共享变量,下一篇文章讲过共享变量后,开始讲开发Spark应用经常遇到的问题,以及如何优化性能。

推荐

动手写Count

从WordCount看Spark大数据处理的核心机制(1)

从WordCount看Spark大数据处理的核心机制(2)

RDD粗粒度转换的威力

查看《Spark大数据处理》系列文章,请进入YoyaProgrammer公众号,点击 核心技术,点击 Spark大数据处理。

分类 Spark大数据处理

优雅程序员 原创 转载请注明出处

Spark大数据处理 之 RDD粗粒度转换的威力的更多相关文章

  1. Spark大数据处理 之 从WordCount看Spark大数据处理的核心机制(1)

    大数据处理肯定是分布式的了,那就面临着几个核心问题:可扩展性,负载均衡,容错处理.Spark是如何处理这些问题的呢?接着上一篇的"动手写WordCount",今天要做的就是透过这个 ...

  2. Spark大数据处理 之 动手写WordCount

    Spark是主流的大数据处理框架,具体有啥能耐,相信不需要多说.我们开门见山,直接动手写大数据界的HelloWorld:WordCount. 先上完整代码,看看咋样能入门. import org.ap ...

  3. Spark大数据处理 之 从WordCount看Spark大数据处理的核心机制(2)

    在上一篇文章中,我们讲了Spark大数据处理的可扩展性和负载均衡,今天要讲的是更为重点的容错处理,这涉及到Spark的应用场景和RDD的设计来源. Spark的应用场景 Spark主要针对两种场景: ...

  4. Spark大数据处理技术

    全球首部全面介绍Spark及Spark生态圈相关技术的技术书籍 俯览未来大局,不失精细剖析,呈现一个现代大数据框架的架构原理和实现细节 透彻讲解Spark原理和架构,以及部署模式.调度框架.存储管理及 ...

  5. 《Spark大数据处理:技术、应用与性能优化 》

    基本信息 作者: 高彦杰 丛书名:大数据技术丛书 出版社:机械工业出版社 ISBN:9787111483861 上架时间:2014-11-5 出版日期:2014 年11月 开本:16开 页码:255 ...

  6. 《Spark大数据处理:技术、应用与性能优化》【PDF】 下载

    内容简介 <Spark大数据处理:技术.应用与性能优化>根据最新技术版本,系统.全面.详细讲解Spark的各项功能使用.原理机制.技术细节.应用方法.性能优化,以及BDAS生态系统的相关技 ...

  7. 《Spark大数据处理:技术、应用与性能优化》【PDF】

    内容简介 <Spark大数据处理:技术.应用与性能优化>根据最新技术版本,系统.全面.详细讲解Spark的各项功能使用.原理机制.技术细节.应用方法.性能优化,以及BDAS生态系统的相关技 ...

  8. Spark大数据处理框架入门(单机版)

    导读 引言 环境准备 安装步骤 1.下载地址 2.开始下载 3.解压spark 4.配置环境变量 5.配置 spark-env.sh 6.启动spark服务 7.测试spark stay hungry ...

  9. ceph hadoop spark 大数据处理

    http://docs.ceph.com/docs/giant/cephfs/hadoop/ https://indico.cern.ch/event/524549/contributions/218 ...

随机推荐

  1. du 命令 -目前的目录所占的磁盘空间

    Linux du命令也是查看使用空间的,但是与df命令不同的是Linux du命令是对文件和目录磁盘使用的空间的查看,还是和df命令有一些区别的. 1.命令格式: du [选项][文件] 2.命令功能 ...

  2. AngularJS:实例

    ylbtech-AngularJS:实例 1.返回顶部 1. AngularJS 实例 实例 您可以在线编辑实例,然后点击按钮查看结果. AngularJS 实例 <div ng-app=&qu ...

  3. Oracle 下ASM磁盘总结

    Oracle 下ASM磁盘总结 文章转载: Oracle下创建ASM磁盘总结https://blog.csdn.net/okhymok/article/details/78791841?utm_sou ...

  4. expected declaration specifiers or '...' before string constant

    /work/platform_bus_dev_drv/led_dev.c:52: error: expected declaration specifiers or '...' before stri ...

  5. 2016.7.27 VS搜索正则表达式,在UltraEdit中可选用Perl正则引擎,按C#语法搜索

    表达式 语法 说明 任一字符 . 匹配除换行符外的任何一个字符. 最多 0 项或更多 * 匹配前面表达式的 0 个或更多搜索项. 最多一项或更多 + 匹配前面表达式的至少一个搜索项. 最少 0 项或更 ...

  6. C语言学习笔记--多维数组和多维指针

    1. 指向指针的指针 (1)指针的本质是变量,会占用一定的内存空间 (2)可以定义指针的指针来保存指针变量的地址值 (3)指针是个变量,同样也存在传值调用与传址调用 重置动态空间的大小 #includ ...

  7. java判断姓是否合格 千家姓

    package com.sycx.domain; import java.lang.reflect.Array; public class FirstName { public static bool ...

  8. 通过Excel导入Mysql 超过65535条数据的办法

    1.截取 65534条数据,进行分sheet,然后1个sheet导入一张表,最后进行整合! 2.采用TXT导入方式,TXT的导入暂时没发现限制的数据条数,下午用TXT导入74万条数据成功 3.如果遇到 ...

  9. mysql存储过程@命名变量的区别

    存储过程中@是用来标识每一次运行存储过程都会保存的值.而直接命名是每一次都会初始化的局部变量.@@是用来标识全局变量. CREATE PROCEDURE prc_test ()BEGIN DECLAR ...

  10. ViewPager的使用方法

    首先是 导入jar包   下载地址:android-support-v4.jar 布局文件里添加viewPager布局 [html] view plaincopy <android.suppor ...