1、累加器应用场景
应用背景:在Executor进行计算的过程中,有些变量的值会在Executor多次使用,
每次使用时,就要从driver端拿取一次,就会产生大量的网络IO,而且还会影响计算效率,
此时就可以用广播变量的方式,在计算之前先把数据从driver端广播到相应的Executor端的缓存里,
后期在使用的时候,就可以直接从缓存里拿取进行计算
注意:广播变量过程,必须是把driver端的某个变量的值广播到Executor端
广播变量的值不易过大,否则会占用大量的内存
2、Accumulator版本的Wordcount
通过继承AccumulatorV2实现几个方法(使用到了伴生类):
package com.murphy.Acc
import org.apache.spark.util.AccumulatorV2
import org.apache.spark.{SparkConf, SparkContext}
import scala.collection.mutable
//伴生类
class AccWordCountDemo extends AccumulatorV2[String, mutable.HashMap[String, Int]] {
// 初始值
private val hashAcc = new mutable.HashMap[String, Int]()
// 检测初始值是否为空
override def isZero: Boolean = hashAcc.isEmpty
// copy一个新的累加器
override def copy(): AccumulatorV2[String, mutable.HashMap[String, Int]] = {
val newAcc = new AccWordCountDemo
// 有可能多个task同时往初始值里写值,有可能出现线程安全问题,此时最好加锁
hashAcc.synchronized {
newAcc.hashAcc ++= hashAcc
}
newAcc
}
// 重置累加器
override def reset(): Unit = hashAcc.clear()
// 局部累加
override def add(v: String): Unit = {
hashAcc.get(v) match {
case None => hashAcc += ((v, 1))
case Some(x) => hashAcc += ((v, x + 1))
}
}
// 全局合并
override def merge(other: AccumulatorV2[String, mutable.HashMap[String, Int]]): Unit = {
other match {
case o: AccumulatorV2[String, mutable.HashMap[String, Int]] => {
for ((k, v) <- o.value) {
hashAcc.get(k) match {
case None => hashAcc += ((k, v))
case Some(x) => hashAcc += ((k, x + v))
}
}
}
}
}
// 输出值
override def value: mutable.HashMap[String, Int] = hashAcc
}
//伴生对象
object AccWordCountDemo {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("AccWordCountDemo").setMaster("local[2]")
val sc = new SparkContext(conf)
val rdd = sc.makeRDD(List("a", "c", "a", "d", "a", "c"), 2)
// 调用累加器对象
val acc = new AccWordCountDemo
// 注册累加器
sc.register(acc, "accwc")
// 开始分布式累加
rdd.foreach(acc.add)
println(acc.value)
sc.stop()
}
}
3、自定义排序
自定义排序(二次排序):
应用场景:比如自定义了一个对象,该对象里有多个字段,需要对多个字段进行排序,
如果直接调用sortBy进行排序,会出错,因为sortBy不知道具体的比较规则。
此时可以用Spark提供的自定义排序进行操作,这样我们就可以指定具体的排序规则了