Spark基础入门(01)—RDD
1,基本概念
RDD(Resilient Distributed Dataset) :弹性分布式数据集
它是Spark中最基本的数据抽象,是编写Spark程序的基础。简单的来讲,一个Spark程序可以概括为:
<输入> => [转换] => <输出>
输入和输出是必须要有的,转换是大部分情况下都有的,将这个过程细化一下,放到Spark中,大概是这样:
(1)输入
一个或多个数据源作为输入,数据源可以是:本地文件,hdfs,数据库,程序中构造的数据集等等,在Spark中,会被抽象为不同的RDD
(2)转换
对输入的RDDs进行一些逻辑处理,由RDDs转换为新的RDDs,实现这个过程可以通过调用RDD的Transformation方法
(3)输出
将最终生成的结果RDD输出,例如:输出到数据库,写入到文件,在程序中转换为集合,数组等等,实现这个过程也是通过调用RDD的Action方法
当然,一个Spark程序中,输入、转换、输出都可能是多个,Transformation,Action算子有哪些这里就不列举了,随便一搜一大堆,这里只说一下两者的区别:Transformation算子只有在碰到Action算子时,才会真正执行,有点像JavaBean的Builder
2,特性
作为一个可以处理大数据量的分布式计算引擎,在兼顾执行性能和开发者体验的同时,这几个问题是很难避开的:
(1)支持多种数据源
RDD提供了不同的实现,可以从不同的存储中读取数据,也可以自定义RDD来实现数据读取
(2)数据吞吐量
RDD通过Partition来控制并发,每个分区会由Spark启动一个Task来进行计算,开发者只需要开发具体的开发逻辑,无须关注调度细节
Spark规定,RDD数据只读,不可更改,更改数据只能生成一个新的RDD,提高调度性能
所有的Transformation操作,数据都是在内存中,并不会落盘,只有在shuffle时会落盘,整个流程形成了一个数据处理的管道,举个例子:
//假如有3个rdd,对输入rdd产生的数据进行了处理
Iterator<Row> transform(Iterator<Row> iterator){
Iterator<Row> ite = f1(iterator)//rdd1
ite = f2(ite)//rdd2
return f3(ite)//rdd3
}
即使调用了transform
方法,也不会触发计算(排除f1, f2, f3生成的数据直接放内存中,例如:list.iterator()),只有在最终输出时才会进行计算:
Iterator<Row> iterator = transform(inputIterator);
while(iterator.hasNext){
iterator.next();
//...
}
//此时会依次触发f3、f2、f1转换逻辑
因此整个过程是在内存中进行的,因此计算速度会很快,并且开发者只需要关注自己的转换逻辑即可
(3)任务容错
在大数据计算过程中,因为执行时间长,数据量大,因此容易出现执行失败的情况,Spark可以对失败的任务进行重试
(4)开发工具
支持缓存rdd,方便多次使用rdd时,无须进行重复计算
提供必要的抽象,让开发者专注于业务逻辑,易于上手(熟悉原理使用效果更佳~)
3,基础方法
3.1 基础方法
那么一个RDD最基本的信息是什么?
我们通过实现一个最最基础的自定义RDD,必须实现的方法有2个:
class TestRDD(sc : SparkContext) extends RDD[Row](sc, Nil){
override def compute(split: Partition, context: TaskContext): Iterator[Row] = {
Iterator()
}
override protected def getPartitions: Array[Partition] = {
Array()
}
}
因此,最基础的方法毫无疑问也是这两个:
getPartitions
:获取所有的分区信息
compute
:计算每个分区的结果,返回迭代器
没错,虽然spark相当复杂,但是最基础的东西,往往都非常简单、朴素,将这两个方法带入:
【<输入> => [转换] => <输出>】,这个流程中,仔细想想,这两个方法是不是就可以完成很多工作?
RDD提供的几个基础方法:
final def getNumPartitions //获取分区总数(基于getPartitions实现)
final def iterator(split: Partition, context: TaskContext): Iterator[T] //获取某个分区计算结果(基于compute实现)
def map[U: ClassTag](f: T => U): RDD[U] //将当前rdd转换成新的rdd(基于iterator实现)
def flatMap[U: ClassTag](f: T => TraversableOnce[U]): RDD[U] // 将当前rdd转换成新的rdd,(基于iterator实现)
def repartition(numPartitions: Int)(implicit ord: Ordering[T] = null): RDD[T] //重新分区(基于getPartitions、compute实现)
当然,还有很多很多实现,可以看org.apache.spark.rdd.RDD
源码,里面有详细介绍
3.2 进阶方法
大部分框架都是这样,先用一些实用,并且使用简单的方法让人可以快速上手,但是这些基础功能只是冰山一角,RDD也是这样,也有一些高阶功能:
protected def getDependencies: Seq[Dependency[_]] = deps //获取rdd的依赖关系,也可以在继承RDD时初始化
protected def getPreferredLocations(split: Partition): Seq[String] = Nil //返回分区最佳的计算主机地址,主要用来分配任务,保证任务尽量在本机计算,减少网络传输
val partitioner: Option[Partitioner] = None //自定义分区,可以为每个key分配对应的分区
这些方法也有很多地方可以使用,这里暂不展开,有个印象即可
4,依赖
首先,我们需要再明确一下,一个Spark程序,有如下的执行流程:
<输入> => [转换] => <输出>
流程中每一个节点,都是1~N个RDD构成,那么RDD之间,就需要有依赖关系,才能保证Spark能正确的按照流程进行执行
根据2.2的介绍,可以知道,RDD的依赖,通过getDependencies
定义,那么我们使用伪代码,则有2种情况:
R1 => R2 => R3
R2的实现为:
def getDependencies = Seq(R1)
(R0, R1) => R2 => R3
R2的实现为:
def getDependencies = Seq(R1, R2)
这个是RDD级别的依赖关系,但是请注意,RDD是有分区的,所以这个依赖关系还需要细化
4.1 依赖关系定义
细化依赖关系之前,还需要说明一下在Spark中,依赖的定义:
abstract class Dependency[T] extends Serializable {
def rdd: RDD[T]
}
它有2个实现:
ShuffleDependency
:被称为宽依赖,这种也是最复杂的,需要进行shuffle操作
NarrowDependency
:被称为窄依赖,它有3个实现:
OneToOneDependency
:很容易理解,用来处理1:1这种依赖关系
RangeDependency
,PruneDependency
(Spark内部使用):处理N:N这种依赖关系
4.2 分区依赖关系
为了简化流程,我们这里使用:R1=>R2,这种单一依赖来进行说明,多依赖情况是类似的,Spark中,将分区依赖分为以下4种情况:
(1)1:1
每个子分区,都来源于同一个父分区(通过:OneToOneDependency
实现)
(2)N:1
1个子分区,由多个父分区组成,可以通过:RangeDependency
实现
(3)N:N
1个子分区,由多个父分区组成,同时1个父分区(所有数据)也为多个子分区提供数据,可以通过:RangeDependency
实现
注意:N:1其实也是N:N的一种,N:N想象空间就很大了,一般是使用:RangeDependency
来实现,Spark的UnionRDD就是N:1
(4)N:N (shuffle)
1个子分区,由多个父分区组成,同时1个父分区(与(3)不同,这里是:部分数据)也为多个子分区提供数据(通过ShuffleDependency
)
4.3 总结
因此,在Spark的依赖体系中,大致可总结如下:
Dependency
ShuffleDependency (N:N)
NarrowDependency
PruneDependency (N:N, 内部使用)
OneToOneDependency (1:1)
RangeDependency (N:N)
5 总结
RDD作为Spark核心中的核心,顶级抽象类,要了解它的设计,个人的学习步骤如下:
(1)明确它的核心作用,从定义可以看到:弹性分布式数据集
(2)了解它的设计理念
(3)在实践中,分析它需要解决的具体问题,搞清楚它的功能边界,哪些是必须它解决的,哪些是可以通过其他方法解决的
(4)循环(2),(3)两步相互印证
Spark基础入门(01)—RDD的更多相关文章
- 【原创 Hadoop&Spark 动手实践 5】Spark 基础入门,集群搭建以及Spark Shell
Spark 基础入门,集群搭建以及Spark Shell 主要借助Spark基础的PPT,再加上实际的动手操作来加强概念的理解和实践. Spark 安装部署 理论已经了解的差不多了,接下来是实际动手实 ...
- CSS3基础入门01
CSS3 基础入门 01 前言 相对于css2来说,css3更新了很多的内容,其中包括选择器.颜色.阴影.背景.文本.边框.新的布局方案.2d.3d.动画等等. 而如果想要学习css3的诸多部分,不妨 ...
- 075 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 07 综合案例-数组移位-主方法功能4的实现
075 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 07 综合案例-数组移位-主方法功能4的实现 本文知识点:综合案例-数组移位-主方法功能4的实现 说明:因为 ...
- 074 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 06 综合案例-数组移位-主方法功能3的实现
074 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 06 综合案例-数组移位-主方法功能3的实现 本文知识点:综合案例-数组移位-主方法功能3的实现 说明:因为 ...
- 073 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 05 综合案例-数组移位-主方法功能1和2的实现
073 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 05 综合案例-数组移位-主方法功能1和2的实现 本文知识点:综合案例-数组移位-主方法功能1和2的实现 说 ...
- 072 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 04 综合案例-数组移位-在指定位置处插入数据方法
072 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 04 综合案例-数组移位-在指定位置处插入数据方法 本文知识点:综合案例-数组移位-在指定位置处插入数据方法 ...
- 071 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 03 综合案例-数组移位-显示数组当中所有元素的的方法
071 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 03 综合案例-数组移位-显示数组当中所有元素的的方法 本文知识点:综合案例-数组移位-显示数组当中所有元素 ...
- 070 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 02 综合案例-数组移位-从键盘接收数据
070 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 02 综合案例-数组移位-从键盘接收数据 本文知识点:综合案例-数组移位-从键盘接收数据 说明:因为时间紧张 ...
- 069 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 01 综合案例-数组移位-案例需求
069 01 Android 零基础入门 01 Java基础语法 09 综合案例-数组移位 01 综合案例-数组移位-案例需求 本文知识点:综合案例-数组移位-案例需求 说明:因为时间紧张,本人写博客 ...
- 068 01 Android 零基础入门 01 Java基础语法 08 Java方法 06 参数传递问题——基本数据类型传值
068 01 Android 零基础入门 01 Java基础语法 08 Java方法 06 参数传递问题--基本数据类型传值 本文知识点:参数传递问题--基本数据类型传值 说明:因为时间紧张,本人写博 ...
随机推荐
- .NET性能优化-推荐使用Collections.Pooled(补充)
简介 在上一篇.NET性能优化-推荐使用Collections.Pooled一文中,提到了使用Pooled类型的各种好处,但是在群里也有小伙伴讨论了很多,提出了很多使用上的疑问. 所以特此写了这篇文章 ...
- AcWing-1022
题解借鉴两位大佬的解析 墨染空 && 野生铅笔 本题是一道 01背包 的扩展题 -- 二维费用01背包问题 把 野生宝可梦 看做物品,则捕捉他需要的 精灵球 个数就是第一费用,战斗皮神 ...
- ExtJS 布局-Accordion布局(Accordion layout)
更新记录: 2022年6月2日 开始. 2022年6月3日 发布. 1.说明 accordion(手风琴)布局一次仅显示一个子组件,内置支持 折叠 和 展开.当需要堆叠多个子组件,并每次只显示一次时, ...
- php 图片转换二进制数
$image = "1.jpg"; //图片地址 $fp = fopen($image, 'rb'); $content = fread($fp, filesize($image) ...
- 你是否有一个梦想?用JavaScript[vue.js、react.js......]开发一款自定义配置视频播放器
前言沉寂了一周了,打算把这几天的结果呈现给大家.这几天抽空就一直在搞一个自定义视频播放器,为什么会有如此想法?是因为之前看一些学习视频网站时,看到它们做的视频播放器非常Nice!于是,就打算抽空开发一 ...
- React技巧之检查元素是否可见
原文链接:https://bobbyhadz.com/blog/react-check-if-element-in-viewport 作者:Borislav Hadzhiev 正文从这开始~ 总览 在 ...
- jenkins部署docker
1. 先在jenkins上配置拉取代码部分,需要在git上找到项目位置,直接复制url即可 http://192.168.0.161:3000/IT-Insurance/Back.Test-Walle ...
- Git下载(快速快速快速下载!!)
在安装Git环境的时候,需要下载Git的安装包,但是官网的下载网速实在是太慢的(几十M的安装包,下载速度只有几十K) (所以可以在镜像中下载,速度超快) Git镜像下载链接-------------- ...
- 解决方案:可以ping别人,但是别人不能ping我
背景:我在写分布式爬虫项目时遇到了slave端无法ping通我的master,我的master可以ping通slave.我将master的防火墙关闭后slave可以ping了,但是这不是解决办法.于是 ...
- AI 企业多云存储架构实践 | 深势科技分享
2020 年末,谷歌旗下 DeepMind 研发的 AI 程序 AlphaFold2 在国际蛋白质结构预测竞赛上取得惊人的准确度,使得" AI 预测蛋白质结构"这一领域受到了空前的 ...