Spark总结之RDD(七)
1. 背景
- Spark作为大数据分布式处理引擎,在设计思想上很大参考了mapreduce的设计思想,但在编程便利性上做了更高层级的抽象,屏蔽了很多分布式计算的细节。具体体现在编程接口的抽象设计上,如RDD、dataSet、dataFrame、DStream等
- Spark本身分为SaprkCore,包含RDD、Accumulators、broadCast,以及内部运行机制,在此之上,有更高层级的抽象,如Spark SQL、Spark Streaming、MLib、Graphx等四大部分,分别是sql、流式处理、机器学习、图计算。
- http://spark.apache.org/

- RDD属于Spark最基础的计算抽象,更上层的编程本质还是转换为RDD来处理,RDD再转换为物理执行计划,分布式运行。
2. 案例
- 本文是关于分布式累加器的文章,实际生产中,有时候需要对数据做统计,这时候可以使用类似过滤器,count等操作组合做统计.
- spark还提供了accumulator分布式累加器来提供更便捷功能.
2.1 longAccumulator
- 顾名思义,这是对于整数的累加器,可以对整数进行累加.
- 实际使用时,可以根据需要累加1,也可以累加其他数字.
- 注意,这里的本质是闭包,是一个class,每个task都会有一个自己的累加器对象,最后的结果是这些累加器汇总到一起之后的结果
- 在spark的分布式计算中,如果使用闭包,最后在executor进行反序列化时会有2种结果,一种是class,这时候一般是一个task一个实例.因为task最终是在executor的线程池中在线程中执行的;一种是object,这时候executor中是只会反序列化一个单例对象(scala的object就是静态对象,只有一个)
- 代码
object AccumulatorTest1 {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("AccumulatorTest1").setMaster("local")
val sc = new SparkContext(conf)
val rdd1: RDD[Int] = sc.makeRDD(List(1, 2, 3, 4, 5, 6, 7, 8, 9))
val longAcc: LongAccumulator = sc.longAccumulator("longAcc")
rdd1.foreach(ele=>{
if(ele % 2 == 0) {
longAcc.add(1L)
}
})
val count: Long = longAcc.count
val value: lang.Long = longAcc.value
println(count)
println(value)
sc.stop()
}
}
运行结果
4
4
2.2 doubleAccumulator
- 代码
object AccumulatorTest2 {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("AccumulatorTest1").setMaster("local")
val sc = new SparkContext(conf)
val rdd1: RDD[Double] = sc.parallelize(List(1.1, 2.2, 3.3, 4.4, 5.5))
val doubleAcc: DoubleAccumulator = sc.doubleAccumulator("doubleAcc")
// 累加器并不会影响数据要做的操作,只是做数据统计
val resRDD: RDD[Double] = rdd1.map(ele => {
if (ele > 3) {
doubleAcc.add(ele)
}
ele + 10
})
println(resRDD.collect().toBuffer)
println("value: " + doubleAcc.value)
println("count: " + doubleAcc.count)
sc.stop()
}
}
运行结果
ArrayBuffer(11.1, 12.2, 13.3, 14.4, 15.5)
value: 13.2
count: 3
- 源码



2.3 collectionAccumulator
- 实际生产中,如果需要对数据做统计汇总,并且拿回来,这时候一般使用这个集合累加器,可以将所需要的数据以集合形式取回
- 注意,所有的累加器最后都是会返回到driver端的内存中,所以累加器不适合存储大量数据,顾名思义,适合进行数据累加
- 如果真的需要对数据做大量统计汇总,可以使用日志或者文件工具,将这些数据写入到文件,数据库或者hdfs中.
object AccumulatorTest3 {
def main(args: Array[String]): Unit = {
val conf: SparkConf = new SparkConf().setAppName("AccumulatorTest1").setMaster("local")
val sc = new SparkContext(conf)
val rdd1: RDD[Any] = sc.parallelize(List(1.1, "20", 2.2, "30", 3.3, "40", 4.4, "50", 5.5))
val collectionAcc: CollectionAccumulator[String] = sc.collectionAccumulator[String]("collectionAcc")
val resRDD: RDD[String] = rdd1.map(ele => {
if (ele.isInstanceOf[String]) {
collectionAcc.add(ele.asInstanceOf[String])
}
ele.toString
})
println(resRDD.collect().toBuffer)
val accValue: util.List[String] = collectionAcc.value
import scala.collection.JavaConverters._
for(e <- accValue.asScala){
println(e)
}
sc.stop()
}
}
运行结果
ArrayBuffer(1.1, 20, 2.2, 30, 3.3, 40, 4.4, 50, 5.5)
20
30
40
50
- 源码


可以看到,这是class,在使用时,在driver端创建,但实际执行是在executor端执行,所以形成了一个闭包
注意,在分布式计算中,如果要做数据做处理,可以一个executor中创建一个对象,多个task共享这个对象来做处理,但这样就需要加锁
但更有效的一种方式就是分布式思想,每个task有自己的对象,最终结果就是所有task的结果进行汇总.

1261

被折叠的 条评论
为什么被折叠?



