Learning Spark阅读笔记1
Programming with RDDs
一个RDD就是一个分布式的元素集合,在Spark中,所有的工作可以表示为要么创建新的RDDs,要么转换现有的RDDs,要么在RDDs上操作计算结果。Spark会自动地分布你的数据到集群上,并行地计算。
RDD基础
每个RDD分成很多partitions,这些partitions可能在集群的不同节点上。
用户创建RDDs有两种方式:
- 加载外部的数据集
- 在驱动程序中分布对象的集合
一旦被创建,RDDs提供两种操作:transdormations和actions。
默认状态,RDDs每次在运行action时都要重新计算。也可以使用RDD.persist()
保存一下结果,可以保存在不同的地方。总结一下,每个Spark程序都要经过一下步骤:
- 从外部数据创建RDDs
- Transform去定义新的RDDs
- 使用persist()去存储需要重复使用的中间RDDs
- 执行actions。
下面将详细讲述上面的四个步骤。
创建RDDs
最简单的方式是将一个存在的集合传递给SparkContext中的parallelize方法。但是不是普遍使用,因为需要将整个数据集存储在一个机器的内存中。
val lines = sc.parallelize(List("pandas", "i like pandas"))
更常见的方式是从外部数据创建RDDs,例如加载一个文本文件为字符串的RDD。
val lines = sc.textFile("/path/to/README.md")
RDD操作
Transformations: 返回一个新的RDD。
Actions:返回给驱动程序一个结果,或者写到存储的地方。
当你使用transformations互相之间创建RDDs时,Spark会跟踪不同RDD之间的依赖关系,叫做lineage graph。Spark会利用这个信息去计算每一个RDD,也可以恢复数据。
在大多数场景中,RDDs太大以致于不能执行collect(),这时需要将数据写入到分布式文件系统中。
注意:每次我们执行新的action,整个RDD都要重新开始计算,为了有效率,可以使用persist存储中间结果。
在Spark中传递函数
在传递函数时要注意函数里面的数据引用需要可被序列化的(实现了java的Serializable接口),同时,要注意避免传递整个对象(需要传递的数据在这个对象中),利用一个局部变量来避免,例如:
class SearchFunctions(val query: String) {
def isMatch(s: String): Boolean = {
s.contains(query);
}
def getMatchesFunctionReference(rdd: RDD[String]): RDD[String] = {
// Problem: "isMatch" mans "this.isMatch", so we pass all of "this"
rdd.map(isMatch)
}
def getMatchesFieldReference(rdd: RDD[String]): RDD[String] = {
// Problem: "query" means "this.query", so we pass all of "this"
rdd.map(x => x.split(query))
}
def getMatchesNoReference(rdd: RDD[String]): RDD[String] = {
// Safe: extract just the field we need into a local variable
val query_ = this.query
rdd.map(x => x.split(query_))
}
Persistence
默认的persist()将会把数据非序列化存储在JVM的堆中。如果我们将数据写入磁盘或者off-heap storage,将会是序列化的。有以下的persist层次:
- MEMORY_ONLY
- MEMORY_ONLY_SER
- MEMORY_AND_DISK
- MEMORY_AND_DISK_SER
- DISK_ONLY
如果你尝试cache很多数据到内存中,Spark将会自动地抛弃老的partitions,使用的是Least Recently Used策略。