帅气的目录
1、共享变量(Shared Variables)
- 通常,在向Spark传递函数(如map或reduce)时,可以使用驱动器中定义的变量,集群中每个任务都会得到该变量的独立副本,这些副本的更新不会回传给驱动器
- 对此,Spark提供了共享变量的功能,下面介绍2个常用:累加器和广播变量
2、累加器(Accumulator)
- 累加器支持并行
2.1、Spark自带累加器
//创建累加器
import org.apache.spark.util.LongAccumulator
val a: LongAccumulator = sc.longAccumulator("a")
//遍历数据
val r = sc.makeRDD(Seq(4, 3, 2, 1), 2)
r.foreach(a.add(_))
//打印累加器
print(a)
//LongAccumulator(id: 0, name: Some(a), value: 10)
2.2、自写累加器
2.2.1、累加器的基类
- AccumulatorV2源码如下,是一个抽象类,需要被实现
- 功能:累积 IN类型 的输入,产生 OUT类型 的输出
OUT类型的读取 应符合 原子性(如:Int、Long)或 线程安全(如:synchronized collections)
abstract class AccumulatorV2[IN, OUT] extends Serializable
IDE小技巧
2.2.2、完整示例1:词频统计

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.util.AccumulatorV2
import scala.collection.mutable
object Hello {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("a1").setMaster("local[2]")
val sc = new SparkContext(conf)
// 创建累加器对象
val a: A = new A()
// 注册累加器对象
sc.register(acc = a, name = "Aa")
// 累加
val r = sc.makeRDD(Seq("a", "a", "b", "a", "a", "b"))
r.foreach(a.add)
// 打印累加器对象的值
println(a) // A(id: 0, name: Some(Aa), value: Map(b -> 2, a -> 4))
}
}
// 自定义累加器,继承AccumulatorV2,光标停在A,并按【Alt+Enter】实现方法
class A extends AccumulatorV2[String, mutable.Map[String, Int]] {
// 定义OUT:可变映射
var m: mutable.Map[String, Int] = mutable.Map[String, Int]()
// 判断是否为初始化状态,映射为空则返回true
override def isZero: Boolean = m.isEmpty
// 复制对象
override def copy(): AccumulatorV2[String, mutable.Map[String, Int]] = {
new A()
}
// 重置
override def reset(): Unit = m.clear()
// 累加
override def add(v: String): Unit = {
m(v) = m.getOrElse(v, 0) + 1
}
// 合并累加器
override def merge(other: AccumulatorV2[String, mutable.Map[String, Int]]): Unit = {
m ++= other.value.map(t => t._1 -> (t._2 + m.getOrElse(t._1, 0)))
}
// 累加器的值
override def value: mutable.Map[String, Int] = m
}
2.2.3、完整示例2:求平均

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.util.AccumulatorV2
object Hello {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("a1").setMaster("local[2]")
val sc = new SparkContext(conf)
// 创建累加器对象
val a: A = new A()
// 注册累加器对象
sc.register(acc = a, name = "Aa")
// 累加
val r = sc.makeRDD(Seq(1, 2, 3, 4, 5, 6, 7, 8, 9))
r.foreach(a.add)
// 打印累加器对象的值
println(a) // A(id: 0, name: Some(Aa), value: 5)
}
}
class A extends AccumulatorV2[Int, Int] {
// 定义OUT:可变映射
var sum: Int = 0
var num: Int = 0
// 判断是否为初始化状态,映射为空则返回true
override def isZero: Boolean = sum == 0 && num == 0
// 复制对象
override def copy(): A = {
new A()
}
// 重置
override def reset(): Unit = {
sum = 0
num = 0
}
// 累加
override def add(s: Int): Unit = {
sum += s
num += 1
}
// 合并累加器
override def merge(other: AccumulatorV2[Int, Int]): Unit = {
other match {
case o: A =>
sum += o.sum
num += o.num
}
}
// 累加器的值
override def value: Int = sum / num
}
2.2.4、完整示例3:分组求平均

import org.apache.spark.{SparkConf, SparkContext}
import org.apache.spark.util.AccumulatorV2
import scala.collection.mutable
object Hello {
def main(args: Array[String]): Unit = {
val conf = new SparkConf().setAppName("a1").setMaster("local[*]")
val sc = new SparkContext(conf)
// 创建累加器对象
val a: A = new A()
// 注册累加器对象
sc.register(acc = a, name = "Aa")
// 累加
val r = sc.makeRDD(Seq(("a", 2), ("b", 7), ("b", 9), ("a", 1), ("a", 3)), 2)
r.foreach(a.add)
// 打印累加器对象的值
println(a) // A(id: 0, name: Some(Aa), value: Map(b -> 8, a -> 2))
}
}
class A extends AccumulatorV2[(String, Int), mutable.Map[String, Int]] {
// 定义OUT:可变映射
val s: mutable.Map[String, Int] = mutable.Map[String, Int]()
val c: mutable.Map[String, Int] = mutable.Map[String, Int]()
// 判断是否为初始化状态,映射为空则返回true
override def isZero: Boolean = s.isEmpty && c.isEmpty
// 复制对象
override def copy(): AccumulatorV2[(String, Int), mutable.Map[String, Int]] = {
new A()
}
// 重置
override def reset(): Unit = {
s.clear()
c.clear()
}
// 累加
override def add(t: (String, Int)): Unit = {
s(t._1) = s.getOrElse(t._1, 0) + t._2
c(t._1) = c.getOrElse(t._1, 0) + 1
}
// 合并累加器
override def merge(other: AccumulatorV2[(String, Int), mutable.Map[String, Int]]): Unit = {
other match {
case o: A =>
s ++= o.s.map(t => t._1 -> (t._2 + s.getOrElse(t._1, 0)))
c ++= o.c.map(t => t._1 -> (t._2 + c.getOrElse(t._1, 0)))
}
}
// 累加器的值
override def value: mutable.Map[String, Int] = {
s.map(t => t._1 -> (t._2 / c.getOrElse(t._1, -1)))
}
}
3、广播变量(Broadcast Variables)
-
Broadcast variables allow the programmer to keep a read-only variable cached on each machine rather than shipping a copy of it with tasks.
- 广播变量允许在每台机器上缓存一个只读变量,而非在每个任务中附带一个独立副本
3.1、示例
需求:过滤不在s中的数据
val s = Set("b", "c")
val r0 = sc.makeRDD(Seq("b", "a", "b", "c", "e", "f", "c"))
不使用广播变量
val r1 = r0.filter(s.contains)
println(r1.collect.toList) //List(b, b, c, c)
使用广播变量,更节省内存
//创建广播变量
import org.apache.spark.broadcast.Broadcast
val b: Broadcast[Set[String]] = sc.broadcast(s)
//使用广播变量
val r2 = r0.filter(b.value.contains)
println(r2.collect.toList) //List(b, b, c, c)

appendix
| en | 🔉 | cn |
|---|---|---|
| implement | ˈɪmplɪmənt | vt. 实施;实现;n. 器具;手段 |
| synchronized | ˈsɪŋkrənaɪzd | 同步的 |
| ship | ʃɪp | n. 船;v. (用船、飞机、卡车)运送;上船; |

839



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



