RDD基础
RDD包含两种操作:
转化操作:由一个 RDD 生成一个新的 RDD,采取惰性求值策略:不会马上进行运算,直到下一个行动操作才会运算
行动操作:对 RDD 计算出一个结果,并把结果返回到驱动器程序中,或把结果存储到外部存储系统(如 HDFS)中
惰性求值
虽然你可以在任何时候定义新的 RDD,但 Spark 只会惰性计算这些 RDD。它们只有第一次在一个行动操作中用到时,才会真正计算
例如:
val inputRDD = sc.textFile("log.txt") val errorsRDD = inputRDD.filter(line => line.contains("error")) //转化操作,不会进行求值 val errorsRDD.count() //行动操作,进行运算
数据持久化:
RDD一般存储在外存上,如果一个RDD会在后续被多次使用,就可以把RDD保存到内存(持久化),使用的是persist()。同时也可以把数据存储到外存,使用 saveAsTextFile()、saveAsSequenceFile()。
创建RDD
创建RDD有两种方式:
- :读取外部数据集
val lines = sc.textFile("/path/to/README.md")
- 在驱动器程序中对一个集合进行并行化,运用parallelize()方法
val lines = sc.parallelize(List("pandas", "i like pandas"))
RDD 操作
转化操作
转化操作从已有的 RDD 中派生出新的 RDD,Spark 会使用谱系图(lineage graph)来记录这些不同 RDD 之间的依赖关系。Spark 需要用这些信息来按需
计算每个 RDD,也可以依靠谱系图在持久化的 RDD 丢失部分数据时恢复所丢失的数据。
行动操作
转化操作把最终求得的结果返回到驱动器程序,或者写入外部存储系统中。由于行动操作需要生成实际的输出,它们会强制执行那些求值必须用到的 RDD 的转化操作。
向Spark 传递函数
Spark 的大部分转化操作和一部分行动操作,都需要依赖用户传递的函数来计算。
def containsError(s):
return "error" in s
word = rdd.filter(containsError)
在传递函数时需要注意两个问题:
- 确保所传递的函数可以被序列化,Spark中在把函数传给executer时是通过序列化格式的数据传输的,所以传输之前需要把函数转为序列化格式,如果在 Scala 中出现了 NotSerializableException的错误,就是不可序列化。
- 传递函数时别把函数所在的对象也传过去,如果你所传递的函数中用到了所在对象中的变量,或者是对象本身的引用(如self.field),就会把整个对象传过去。
class SearchFunctions(object): def __init__(self, query): self.query = query def isMatch(self, s): return self.query in s def getMatchesFunctionReference(self, rdd): # 问题:在"self.isMatch"中引用了整个self return rdd.filter(self.isMatch) def getMatchesMemberReference(self, rdd): # 问题:在"self.query"中引用了整个self return rdd.filter(lambda x: self.query in x)
正确的做法(python版)
class WordFunctions(object): ... def getMatchesNoReference(self, rdd): # 安全:只把需要的字段提取到局部变量中 query = self.query return rdd.filter(lambda x: query in x)
常见的转化操作和行动操作
转化操作:
行动操作