RDD 介绍

spark 最重要的一个概念叫 RDD,Resilient Distributed Dataset,弹性分布式数据集,它是 spark 的最基本的数据(也是计算)抽象。

代码中是一个抽象类,它代表一个 不可变、可分区、里面的元素可并行计算的数据集合。

RDD 的属性

拥有一组分区:数据集的基本组成单位

拥有一个计算每个分区的函数

拥有一个分区器,partitioner,即 RDD 的分片函数

RDD 间存在依赖关系    【下面 RDD 特点中有解释】

拥有一个列表,存储每个 partitioner 的优先位置

优先位置

这里有个概念叫移动数据不如移动计算,之前在 hadoop 中接触过这个概念,不多解释;

RDD 也是分区存储的,那么 RDD 的数据到底存储在哪个节点,必须有个记录;

当有请求过来时,为了提高运行效率,我们需要把任务下发到存储所需数据的节点,这样避免了网络传输

RDD 的特点

只读:无法进行更改,也就是不可变,如果需要改动,只能创建一个新的 RDD,也就是从一个 RDD 转换成另一个 RDD

依赖关系:一个 RDD 由另一个 RDD 转换得到,也就是 一个 RDD 依赖另一个 RDD,而且可以多层依赖,为了便于理解和描述,我们有时把依赖关系也叫血缘关系;

  当 血缘关系 很长时,可以通过持久化 RDD 来切断血缘关系;

惰性执行:RDD 的执行是按照血缘关系延时计算的,也就是 惰性计算

依赖关系

依赖关系有两种

窄依赖:一对一;RDDs 之间分区 一 一对应,也就是一个分区完全由另一个分区转换得到,而不是由另外几个分区转换得到

宽依赖:多对一;与窄依赖相反,一个分区由另外几个分区转换得到

缓存

缓存是把 RDD 暂时存起来,用于如下场景:

1. 优化效率:一个程序中多次用到一个 RDD,可以先把这个 RDD 缓存起来,避免重复计算

2. 避免出错

distFile = sc.textFile('README.md')
m = distFile.map(lambda x: len(x)) # map 是 转换 操作,并不立即执行
m.cache() # 把 map 的输出缓存到内存中,其实 cache 就是 执行 操作 #或者
m.persist()

RDD 的创建

创建 RDD 有 3 种方式

1. 基于内存:从内存中的数据创建 RDD,并行化已存在的序列

2. 基于外部存储介质:读取外部数据源,比如 hdfs    【外部存储介质中的数据,注意 spark 本身没有存储功能】

3. 由一个 RDD 转换得到:这种方式不多解释

并行化数据集合

并行化集合是通过在一个 迭代器或者集合 上 调用 SparkContext 的 parallelize 方法生成的    【内存中的数据】

可能还有个 makeRDD,我这里实验不成功,makeRDD 封装了 parallelize,功能是一样的

data = range(10000)
distData = sc.parallelize(data)
distData.reduce(lambda a, b: a+b)

为了创建一个能并行操作的分布式数据集,所有元素都将被拷贝;

然后我们可以调用 reduce 进行叠加,累计求和

并行化集合时一个重要的参数是将数据集切分的数量。一个切片对应一个 spark 任务,我们可指定切片数量

distData2 = sc.parallelize(data, 100)   # 切 100 分

外部数据集

由于 spark 本身没有存储功能,一般是从 本地文件、hadoop 等获取外部数据集

本地文件

textFile(name, minPartitions=None, use_unicode=True)[source]
Read a text file from HDFS, a local file system (available on all nodes), or any Hadoop-supported file system URI, and return it as an RDD of Strings. If use_unicode is False, the strings will be kept as str (encoding as utf-8), which is faster and smaller than unicode. (Added in Spark 1.2)

minPartitions 指定最小分区数,也就是说,实际分区数可能比这个数大,在 hdfs 中,文件本身就是 以 block 存储的,此时这个 分区数 不能小于 block 数

示例代码

distFile = sc.textFile('README.md')
distFile = sc.textFile('xx.csv')
distFile = sc.textFile('logs') # 支持文件夹
# textFile("/my/directory/*.txt") # 支持通配符
# textFile("/my/directory/*.gz") # 支持压缩文件 type(sc.textFile('README.md')) # <class 'pyspark.rdd.RDD'> distFile.map(lambda x: int(x[-1])).reduce(lambda a, b: a + b)
distFile.map(lambda x: len(x)).reduce(lambda a, b: a + b) # map 的输入是每一行,map 的作用是求每一行的 len
# reduce 的输入是两个数字,reduce 的作用是求这两个数的和,
# 也就是把 所有行的 len 逐次求当前元素(当前累计 len 的和)与下一元素的和

读取文件后自动生成 RDD;

各种读取方式都支持 多种 文件格式,如 文件夹,通配符、压缩文件

批量读取本地文件

distFile = sc.wholeTextFiles('files')   # 读取 files 文件夹下所有文件内容,返回 (filename, filecontent) 键值对

输入必须是 路径

读取 hdfs 文件

rdd = sc.textFile('hdfs://hadoop10:9000/spark/dwd.csv')

RDD 的读取与存储

读取和存储其实都是一种 action 操作,这里只做简单介绍

rdd.collect()    # 读取
rdd.saveAsPickleFile('path')
rdd.saveAsTextFile('path')

存储路径事先不能存在;Pickle 序列化,编码存储;

RDD 算子

算子:认知心理学中的概念,它认为解决问题其实是将问题的初始状态,通过一系列的操作,对问题状态进行转换,然后达到解决状态,这个一系列操作称为算子。

只是一种高大上的叫法,其实就是 RDD 操作

RDD 支持两种类型的操作:transformations 和 actions

transformations:转换,就是从一种 RDD 转换成 另一种 符合 要求 的 RDD,类似于 map

actions:行动,执行计算,类似于 reduce,返回结果

值得注意的是,transformations 是惰性计算,也就是说 transformations 并不立即执行,只有 actions 时,才会执行 transformations

这使得 spark 高效,以 map-reduce 为例,如果我们 map 并不是目的,reduce 才是目的,那么 map 之后 就计算的话,输出存在哪里呢?

如果存在文件,一浪费时间,二浪费地方,如果我们 reduce 时才执行 map,直接把 map 的庞大输出存入内存即可,甚至 流式 传给 reduce,非常高效。

RDD 分区

我们通过实操来验证 RDD 是如何分区的

parallelize 分区

操作一:local 模式,不指定分区

data = range(5)
distData = sc.parallelize(data)
distData.saveAsPickleFile('output') # 存储路径事先不能存在

输出 4 个分区

_SUCCESS
part-00000 # 存放了 0
part-00001 # 存放了 1
part-00002 # 存放了 2
part-00003 # 存放了 3,4

4 从何而来呢?

[root@hadoop10 ~]# cat /proc/cpuinfo |grep "cores"|uniq
cpu cores : 4

即 CPU 核数,

也就是说,parallelize 默认分区数为 CPU 核数

操作二:local 模式,指定分区

data = range(5)
distData = sc.parallelize(data, 3)
distData.saveAsTextFile('output')

输出 3 个分区

_SUCCESS
part-00000 # 存放了 0
part-00001 # 存放了 12
part-00002 # 存放了 34

也就是说,指定了分区数,就分为多少个区,注意,请指定 大于等于 2 的数,不要瞎搞

操作三:local 模式,指定分区,且分区数大于数据量

data = range(5)
distData = sc.parallelize(data, 6)
distData.saveAsTextFile('output')

输出 6 个分区

_SUCCESS
part-00000 # 存放了 空
part-00001 # 存放了 0
part-00002 # 存放了 1
part-00000 # 存放了 2
part-00001 # 存放了 3
part-00002 # 存放了 4

也就是说,即使数据量不够多个分区瓜分,也会按指定个数生成分区,没分到数据的分区为空

通过源码知悉,parallelize 的分区数优先级 为 自定义 --> max(cpu 核数,2),也就是最小为 2

textFile 分区

这个相对来说比较麻烦,我们做个简单介绍。

首先看下 textFile 源码

可以看到,minPartitions 就是我们自定义的分区数;然后 调用了 hadoopFile 方法并把 minPartitions 传给了 hadoopFile;

minPartitions 的默认值是 调用 defaultMinPartitions 方法,源码如下

它取 defaultParallelism 和 2 的最小值;defaultParallelism 方法 源码如下

spark.default.parallelism 是在 SPARK_HOME/conf/spark-default.conf 中设置的一个参数,默认是没有这个配置的;

所以

1. 如果没有在 textFile 中指定 minPartitions,而且也没有配置 spark.default.parallelism,那么 minPartitions 最大是 2

2. 如果没有在 textFile 中指定 minPartitions,但是配置了 spark.default.parallelism,那么 minPartitions 最大也是 2  【如果没有自定义分区数 minPartitions,那么 minPartitions = min(max(CPU 核数, 2), 2);假设我们认为核数大于  2 ,故 minPartitions = 2】

3.  如果没有在 textFile 中指定 minPartitions,minPartitions 最大是 2

4. 如果在 textFile 中指定了 minPartitions,minPartitions 就是指定数目

接下来,我们把 minPartitions 传给 hadoopFile,hadoopFile 会计算文件(如果有多个文件,计算全部文件大小)大小 totalSize,然后把文件 split 成  minPartitions 分,每个切片大小为 goalSize = totalSize / minPartitions,取整

每个 hadoop block 也是一个 文件,在 hadoop中 block 大小是有 最大值的,比如 64M,也就是说我们不能把文件切分成 大于 block 大小的块,也就是说 切片 取 min(goalSize, blockSize) 作为 真正的 切片大小,

但是 切片太小的话又没有意义了,所以 splitSize = max(minSize,min(goalSize, blockSize)), 而 minSize = max(SPLIT_MINSIZE,1),也就是说 minSize 最小为 1, 于是 splitSize 最小为 1,

分数区计算:totalSize / splitSize,如果整除,就是本身,如果不整除,取整,然后 + 1

那我们来测试下

dataFile = sc.textFile('data.txt')
dataFile.saveAsTextFile('output')

7 字节文件,不指定分区数,结果分为 3 个区;splitSize = 7 / 2 = 3.5 = 3, 分区数 = 7 / 3 = 2 + 1 = 3

8 字节文件,不指定分区数,结果分为  2 个区;splitSize = 8 / 2 =  4,分区数 = 8 / 4 = 2

8 字节文件,指定 4 个分区,splitSize = 8 / 4 = 2,分区数 = 8 / 2 = 4;经测试,正解

参考资料:

https://blog.csdn.net/zjwcsdn123/article/details/80489537  spark中textfile方法对文件的分片

https://www.jianshu.com/p/e33671341f0d  spark通过textFile读取hdfs数据分区数量规则

spark教程(三)-RDD认知与创建的更多相关文章

  1. spark教程(四)-SparkContext 和 RDD 算子

    SparkContext SparkContext 是在 spark 库中定义的一个类,作为 spark 库的入口点: 它表示连接到 spark,在进行 spark 操作之前必须先创建一个 Spark ...

  2. 【项目管理和构建】十分钟教程,eclipse配置maven + 创建maven项目(三)

    [项目管理和构建]十分钟教程,eclipse配置maven + 创建maven项目(三) 上篇博文中我们介绍了maven下载.安装和配置(二),这篇博文我们配置一下eclipse,将它和maven结合 ...

  3. RDD编程 上(Spark自学三)

    弹性分布式数据集(简称RDD)是Spark对数据的核心抽象.RDD其实就是分布式的元素集合.在Spark中,对数据的操作不外乎创建RDD.转化已有RDD以及调用RDD操作进行求值.而在这一切背后,Sp ...

  4. Spark 核心概念 RDD 详解

    RDD全称叫做弹性分布式数据集(Resilient Distributed Datasets),它是一种分布式的内存抽象,表示一个只读的记录分区的集合,它只能通过其他RDD转换而创建,为此,RDD支持 ...

  5. Spark分布式计算和RDD模型研究

    1背景介绍 现今分布式计算框架像MapReduce和Dryad都提供了高层次的原语,使用户不用操心任务分发和错误容忍,非常容易地编写出并行计算程序.然而这些框架都缺乏对分布式内存的抽象和支持,使其在某 ...

  6. Spark学习之RDD编程总结

    Spark 对数据的核心抽象——弹性分布式数据集(Resilient Distributed Dataset,简称 RDD).RDD 其实就是分布式的元素集合.在 Spark 中,对数据的所有操作不外 ...

  7. Spark的核心RDD(Resilient Distributed Datasets弹性分布式数据集)

    Spark的核心RDD (Resilient Distributed Datasets弹性分布式数据集)  原文链接:http://www.cnblogs.com/yjd_hycf_space/p/7 ...

  8. Spark 核心概念RDD

    文章正文 RDD全称叫做弹性分布式数据集(Resilient Distributed Datasets),它是一种分布式的内存抽象,表示一个只读的记录分区的集合,它只能通过其他RDD转换而创建,为此, ...

  9. Spark深入之RDD

    目录 Part III. Low-Level APIs Resilient Distributed Datasets (RDDs) 1.介绍 2.RDD代码 3.KV RDD 4.RDD Join A ...

随机推荐

  1. AcWing:144. 最长异或值路径(dfs + 01字典树)

    给定一个树,树上的边都具有权值. 树中一条路径的异或长度被定义为路径上所有边的权值的异或和: ⊕ 为异或符号. 给定上述的具有n个节点的树,你能找到异或长度最大的路径吗? 输入格式 第一行包含整数n, ...

  2. python-numpy-1

    mean() mean() 函数定义: numpy.``mean(a, axis=None, dtype=None, out=None, keepdims=<class numpy._globa ...

  3. [CSP-S模拟测试]:那一天她里我而去(堆优化Dijkstra)

    题目传送门(内部题3) 输入格式 每个测试点有多组测试数据.第一行有一个正整数T表示数据组数.接下来对于每组数据,第一行有两个正整数n,m分别代表图的点数和边数.接下来有m行,每行三个整数u,v,d表 ...

  4. JS基础_原型对象

    原型prototype 我们创建的每一个函数,解析器都会向函数中添加一个属性prototype 这个属性,对应着一个对象,这个对象就是我们所谓的原型对象 1.如果函数作为普通函数调用prototype ...

  5. [windows菜鸟]Windows API函数大全(完整)

    Windows API函数大全,从事软件开发的朋友可以参考下 1. API之网络函数 WNetAddConnection 创建同一个网络资源的永久性连接 WNetAddConnection2 创建同一 ...

  6. VSCODE开发VUE.JS前端插件

    VUE前端插件.转载自:https://www.cnblogs.com/karthuslorin/p/8577224.html vscode是微软开发的的一款代码编辑器,就如官网上说的一样,vscod ...

  7. flutter vscode 小插件

    dart flutter Awesome Flutter Snippets Bracket Pair Colorizer

  8. 阶段3 3.SpringMVC·_03.SpringMVC常用注解_8 SessionAttributes注解

    SpringMvc提供的Model类 ModelMap继承LinkedHashMap 页面取值 把request这个对象全部输出了. SessionAttribute 取值 从sessionAttri ...

  9. Jmeter测试结果分析(上)

    Jmeter测试结果分析这一篇,我打算分成上下两部分.上篇,主要讲述如何使用jmeter中Assertion对结果进行简单的分类:下篇,主要讲述的是当我们拿到测试结果后,我们应该如何去看待这些测试结果 ...

  10. Windows下查找文件或文件夹被哪个进程占用

    Linux下我们可以使用lsof +D /filepath/,查看到文件被那些进程占用. windows下也会经常遇到文件夹或文件无法删除或无法访问的问题,使用“资源监视器”可以找到占用的进程,可以尝 ...