通过一个简单的单词计数的例子来开始介绍RDD编程。
import org.apache.spark.{SparkConf, SparkContext} object word { def main(args: Array[String]): Unit = { val conf = new SparkConf().setMaster("local").setAppName("word") val sc = new SparkContext(conf) val input = sc.parallelize(List("spark core scala python java spark scala")) val words = input.flatMap(line => line.split(" ")) val counts = words.map(word => (word,1)).reduceByKey{case (x,y) => x + y} counts.foreach(println) } }
使用Scala语言,IDE使用IntelliJ IDEA 。在IDEA上运行Spark应用需要添加Maven依赖。
org.apache.spark spark-core_2.10 1.6.3
运行结果:
运行Spark应用,首先需要导入Spark包,这里使用Maven来连接公共仓库中的Spark包。
接下来创建一个SparkConf来配置应用,这里使用local即本地模式,方便调试代码。
然后基于这个SparkConf创建一个SparkContext对象。
有了SparkContext以后,就可以来访问Spark,创建RDD。sc.parallelize创建了一个Seq对象 RDD,可以对这个RDD进行操作。flatmap和map就是对这个RDD进行的一些操作完成单词计数,这些操作接下来将进行解释,最后将结果在后台显示出来。这样一个简单的Spark应用就完成了。
RDD编程
前一章介绍过 RDD即弹性分布式数据集,是Spark对数据的核心抽象,RDD就是分布式的元素集合。
Spark对数据的操作无外乎创建RDD,转化已有RDD以及调用RDD操作进行求值。Spark会自动将RDD中的数据分发到集群上,并将操作并行化执行。
可以使用两种方法创建RDD:读取一个外部数据集,或在驱动器程序里分发驱动器程序中的对象集合。
例如:SparkContext.textFile()用来读取文本文件作为一个字符串RDD。
lines = sc.textFile("input.txt")
还可以把程序中一个已有的集合传给SparkContext的parallelize()方法 如我们上面单词计数例子里的那样。
RDD创建出来后,可以进行两种类型的操作:转化操作和行动操作。
a、转化操作
RDD的转化操作是返回新的RDD的操作,转化出来的RDD是惰性求值的,只有在行动操作中用到这些RDD时才会被计算。
常见转化操作有:
b、行动操作
行动操作把最终求得的结果返回到驱动器程序或者写入外部存储系统中。
常见的行动操作有:
val input = sc.parallelize(List(1,2,3,4,5,6,7,8,9,10))
val result = input.aggregate((0,0))(
(acc , value) => (acc._1 + value , acc._2 + 1)
(part1 , part2) => (part1._1 + part2._1 , part1._2 + part2._2))
val avg = result._1 / result._2.toDouble
aggregate()函数计算过程:
value是input中的值 , 即 1 到 10
在分布式计算中,将input分成多个分区,假设3个分区 ,分别计算 (1,2,3) (4,5,6) (7,8,9,10)
第一个分区用(acc , value) => (acc._1 + value , acc._2 + 1) 计算过程,初始值为0:
0 + 1 , 0 + 1
1 + 2 , 1 + 1
2 + 3 , 2 + 1
最终这个分区得到 (6,3) 即表示和为6,有3个元素。同样,另外两个分区得到(15,3) (34,4)
接着调用(part1 , part2) => (part1._1 + part2._1 , part1._2 + part2._2)) 这一步
将三个分区的和以及元素个数加起来得到(55,10)
Spark在每次调用行动操作时都会重算RDD以及它的所有依赖,为了避免多次计算同一个RDD,可以让Spark对数据进行持久化。
持久化数据所在的节点发生故障时,Spark会在需要用到缓存的数据时重算丢失的数据分区。最好的做法是将数据备份到多个节点上。
使用persist()用来把数据以序列化的形式缓存,默认情况下会缓存在JVM的堆空间中。如果缓存的数据太多,内存中放不下,Spark会自动利用最近最少使用的缓存策略把最老的分区从内存中移除。
unpersist()用于手动把持久化的RDD从缓存中移除。