文章目录
一、名词解释
1. RDD
弹性分布式数据集(resilient distributed dataset, 简称RDD)
在Spark
中,对数据的所有操作不外乎创建RDD
、转化已有RDD
以及调用RDD
操作进行求值。
二、Spark核心概念
每个
Spark
应用都由一个驱动器程序(driver program)来发起集群上的各种并行操作。驱动器程序通过一个SparkContext对象来访问Spark。
三、RDD基础
1. 转化操作和行动操作的区别
转化操作和行动操作的区别在于Spark
计算RDD
的方式不同。
虽然可以在任何时候定义新的RDD
,但Spark
只会惰性计算这些RDD
。只有他们第一次在一个行动操作中用到时,才会真正计算。
Spark
了解了完整的转化操作链之后,就可以只计算求结果时真正需要的数据。
RDD
的转化操作是返回一个新的RDD
的操作;而行动操作则是向驱动程序返回结果,或把结果写入外部系统的操作,会触发实际的计算。
如果对于一个特定的函数是属于转化操作还是行动操作感到困惑,可以看看它的返回值类型,转化操作返回的是RDD
,而行动操作返回的是其他的数据类型。
2. 创建RDD
Spark
提供了两种创建RDD
的方式:读取外部数据集,以及在驱动程序中对一个集合进行并行化。
3. 转化操作
通过转化操作,从已有的RDD
中派生出新的RDD
,Spark
会使用谱系图来记录这些不同RDD
之间的依赖关系。
Spark
需要用这些信息来按需计算每个RDD
,也可以依靠谱系图,在持久化的RDD
丢失部分数据时恢复所丢失的数据。
4. 行动操作
由于行动操作需要生成实际的输出,他们会强制执行那些求值必须用到的RDD
的转化操作。
5. 惰性求值
RDD
的转化操作都是惰性求值的。这意味着在被调用行动操作之前,Spark
不会开始计算。
6. 常见的转化操作
序号 | 函数 | 作用 |
---|---|---|
1 | map | 接收一个函数,把这个函数用于RDD 中的每个元素,将函数的返回结果作为结果RDD 中对应元素的值。 |
2 | filter | 接收一个函数,并将RDD 中满足该函数的元素放入新的RDD 中返回。 |
3 | flatMap | 将结果拍平 |
例一:Scala
版计算RDD
中各值的平方
val input = sc.parallelize(List(1, 2, 3, 4))
val result = input.map(x => x * x)
println(result.collect().mkString(","))
我们可以使用Spark
自带的Scala
交互式窗口:
# 切换到spark目录
cd spark-3.0.0-bin-hadoop2.7/
# 打开scala版 spark shell
./bin/spark-shell
看到以下输出时,就已经进入spark shell
啦
Setting default log level to "WARN".
To adjust logging level use sc.setLogLevel(newLevel). For SparkR, use setLogLevel(newLevel).
Spark context Web UI available at http://*.*.*.*:*
Spark context available as 'sc' (master = local[*], app id = local-1623326674668).
Spark session available as 'spark'.
Welcome to
____ __
/ __/__ ___ _____/ /__
_\ \/ _ \/ _ `/ __/ '_/
/___/ .__/\_,_/_/ /_/\_\ version 3.0.0
/_/
Using Scala version 2.12.10 (OpenJDK 64-Bit Server VM, Java 1.8.0_41)
Type in expressions to have them evaluated.
Type :help for more information.
scala>
我设置了log4j
的日志输出级别为ERROR
,所以没有INFO
或者WARN
级别的日志。
设置方式很简单,将
conf/
目录下的log4j.properties.template
文件,拷贝一份,重命名为log4j.properties
,打开log4j.properties
,将log4j.rootCategory
变量设置为ERROR, console
,即可。
例二:Scala
中的flatMap()
将行数据切分为单词
val lines = sc.parallelize(List("hello world", "hi"))
val words = lines.flatMap(_.split(" "))
words.foreach(println)
7. 常见的行动操作
函数名 | 作用 | 示例 |
---|---|---|
collect() | 返回RDD中的所有元素 | rdd.collect() |
count() | RDD中的元素个数 | rdd.count() |
countByValue() | 各元素在RDD中出现的次数 | rdd.countByValue() |
take(num) | 从RDD中返回num个元素 | rdd.take(2) |
top(num) | 从RDD中返回最前面的num个元素 | rdd.top(2) |
reduce(func) | 并行整合RDD中所有数据 | rdd.reduce((x, y) => x + y) |
foreach(func) | 对RDD中的每个元素使用给定的函数 | rdd.foreach(println) |
8. 持久化
为了避免多次计算同一个RDD
,可以让Spark
对数据进行持久化。当我们让Spark
持久化存储一个RDD
时,计算出RDD
的节点会分别保存他们求出的分区数据。
如果一个有持久化数据的节点发生故障,Spark
会在需要用到缓存的数据时重算丢失的数据分区。
在Scala
和Java
中,默认情况下,persist()
会把数据以序列化的形式缓存在JVM
的堆空间中。
我们在第一次对这个RDD
调用行动操作前就调用了persist()
方法。persist()
调用本身不会触发强制求值。
如果要缓存的数据太多,内存放不下,Spark
会自动利用最近最少使用(LRU
)的缓存策略把最老的分区从内存中移除。