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. Java企业版文档地址

    地址:http://docs.oracle.com/javaee/7/index.html

  2. *CodeIgniter框架集成支付宝即时到账SDK

    客户的网站需要支付功能,我们选择了业界用的最多的支付宝即时到账支付.申请了两次将近两周的时间终于下来了,于是我开始着手测试SDK整合支付流程. SDK中的代码并不复杂,就是构造请求发送,接收并验证签名 ...

  3. linux常用查看系统操作的linux命令

    系统# uname -a # 查看内核/操作系统/CPU信息# head -n 1 /etc/issue # 查看操作系统版本# cat /proc/cpuinfo # 查看CPU信息# hostna ...

  4. yum 时一直停在Determining fastest mirrors 界面

    [root@fanyk ~]# yum redis Loaded plugins: fastestmirror Determining fastest mirrors 在yum makecache时, ...

  5. redis宕机时哨兵的处理

    https://blog.csdn.net/a67474506/article/details/50435498 redis宕机是的故障处理 重启故障机 sentinel.conf 的配置会改变

  6. Orcal nvl函数

    NVL(E1, E2)的功能为:如果E1为NULL,则函数返回E2,否则返回E1本身.但此函数有一定局限,所以就有了NVL2函数. 拓展:NVL2函数:Oracle/PLSQL中的一个函数,Oracl ...

  7. LoadRunner脚本编写之二

    LoadRunner脚本编写之二 编程基本语法必须要记牢.程序的思想也很重要. 下面来回顾一下嵌套循环例子. Action() {     int  i,j;   //生命两个变量     for ( ...

  8. body和document的梗

    http://bbs.zhinengshe.com/thread-1199-1-1.html 1. 在空白的页面加点击事件,是加在body上么 ? <!DOCTYPE html> < ...

  9. mysql 索引失效的几种情况+

  10. PHP进阶之路 -- 02 面向对象

    PHP进阶之路-- 之 “面向对象” 基础 概念 类的介绍和定义 类的属性 类常量 类的自动加载 构造函数和析构函数 访问控制 对象继承 范围解析操作符 static静态关键字 抽象类 对象接口 Tr ...