Spark学习-RDD编程基础

1. RDD基础概念

Spark上开发的应用程序都是由一个driver programe构成,这个所谓的驱动程序在Spark集群通过跑main函数来执行各种并行操作。集群上的所有节点进行并行计算需要共同访问一个分区元素的集合,这就是RDD(RDD resilient distributed dataset)弹性分布式数据集。RDD可以存储在内存或磁盘中,具有一定的容错性,可以在节点宕机重启后恢复。

在Spark 中,对数据的所有操作不外乎创建RDD、转化已有RDD 以及调RDD 操作进行求值。而在这一切背后,Spark 会自动将RDD 中的数据分发到集群上,并将操作并行化执行。

2. 创建RDD

创建RDD有两种方式:一种是通过并行化驱动程序中的已有集合创建,另外一种方法是读取外部数据集。

2.1 并行化

一种非常简单的创建RDD的方式,将程序中的一个集合传给SparkContextparallelize()方法。
python中的操作(pyspark打开shell):

>>> data = [1, 2, 3, 4, 5]
>>> distData = sc.parallelize(data)
# 对RDD进行测试操作
# 对集合中的所有元素进行相加,返回结果为15
>>> distData.reduce(lambda a, b: a + b)
15  

scala中的操作(spark-shell打开shell):

scala> val data = Array(1,2,3,4,5)
data: Array[Int] = Array(1, 2, 3, 4, 5)

scala> val distData = sc.parallelize(data)
distData: org.apache.spark.rdd.RDD[Int] = ParallelCollectionRDD[0] at parallelize at <console>:23

scala> distData.reduce((a,b)=>a+b)
res0: Int = 15   

2.2 读取外部数据集

Spark可以从任何Hadoop支持的存储上创建RDD,比如本地的文件系统,HDFS,Cassandra等。Spark可以支持文本文件,SequenceFiles等。
这种方法更为常用。

python:

# 从protocols文件创建RDD
distFile = sc.textFile("/etc/protocols")

scala:

// 从protocols文件创建RDD
val distFile = sc.textFile("/etc/protocols")

RDD操作

RDD支持两种操作:

  1. 转换(transformations):将已存在的数据集转换成新的数据集,例如map。转换是惰性的,不会立刻计算结果,仅仅记录转换操作应用的目标数据集,当动作需要一个结果时才计算。
  2. 动作(actions) :数据集计算后返回一个值给驱动程序,例如reduce

转换操作是懒惰的,举个例子:


>>> lines = sc.textFile("README.md")
>>> pythonLines = lines.filter(lambda line: "Python" in line)
>>>> pythonLines.first()

如果Spark 在我们运行lines = sc.textFile(…) 时就把文件中所有的行都读取并存储起来,就会消耗很多存储空间,而我们马上就要筛选掉其中的很多数据。相反, 一旦Spark 了解了完整的转化操作链之后,它就可以只计算求结果时真正需要的数据。事实上,在行动操作first() 中,Spark 只需要扫描文件直到找到第一个匹配的行为止,而不需要读取整个文件。

下面引用《Spark快速大数据分析》的一段话:

我们不应该把RDD 看作存 放着特定数据的数据集,而最好把每个RDD 当作我们通过转化操作构建出来的、记录如 何计算数据的指令列表。把数据读取到RDD 的操作也同样是惰性的。因此,当我们调用 sc.textFile() 时,数据并没有读取进来,而是在必要时才会读取。和转化操作一样的是, 读取数据的操作也有可能会多次执行。

简单的例子理解转换和行动操作:

python:

# 从protocols文件创建RDD
distFile = sc.textFile("/etc/protocols")

# Map操作,每行的长度,转换操作
lineLengths = distFile.map(lambda s: len(s))

# Reduce操作,获得所有行长度的和,即文件总长度,这里才会执行map运算
totalLength = lineLengths.reduce(lambda a, b: a + b)

# 可以将转换后的RDD保存到集群内存中
lineLengths.persist()

scala:

// 从protocols文件创建RDD
val distFile = sc.textFile("/etc/protocols")

// Map操作,每行的长度
val lineLengths = distFile.map(s => s.length)

// Reduce操作,获得所有行长度的和,即文件总长度,这里才会执行map运算
val totalLength = lineLengths.reduce((a, b) => a + b)

// 可以将转换后的RDD保存到集群内存中
lineLengths.persist()

常见的转换和行动操作

如下图所示:

这里写图片描述
这里写图片描述

4. 向Spark传递函数

4.1 python

  1. 匿名函数:lambda的大量使用,让简单的函数写成表达式的样子。
  2. 模块里的顶层函数
  3. Spark中已调用函数中定义的本地函数
# 定义需要传递给Spark的函数
def myFunc(s):
    words = s.split(" ")
    return len(words)
# 将词数统计函数传递给Spark
sc.textFile("/etc/protocols").map(myFunc).reduce(lambda a,b:a+b)

上述中定义的函数为计算一行字符串中单词的个数,
sc.textFile("/etc/protocols").map(myFunc).reduce(lambda a,b:a+b)表明先读取内容转出RDD,然后转换map()是对每一行都进行统计操作,然后进行行动操作,用lambda匿名函数计算所有行的单词和。

4.2 scala

  1. 匿名函数:常用于短小的代码片段。
  2. 全局单例对象中的静态函数:例如,你可以定义一个object MyFunctions,然后将MyFunctions.func1传入,就像下面这样:
//创建一个单例对象MyFunctions
object MyFunctions {
  def func1(s: String): Int = {s.split(" ").length}
}
val lineLengths = sc.textFile("/etc/protocols").map(MyFunctions.func1)
val count = lineLengths.reduce((a,b)=>a+b)

与python几乎一样,注意其匿名函数的写法。

参考资料:
1. 《spark快速大数据分析》
2. https://www.shiyanlou.com/courses/456/labs/1462/document

  • 3
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值