顾名思义,broadcast 就是将数据从一个节点发送到其他各个节点上去。这样的场景很多,比如 driver 上有一张表,其他节点上运行的 task 需要 lookup 这张表,那么 driver 可以先把这张表 copy 到这些节点,这样 task 就可以在本地查表了。如何实现一个可靠高效的 broadcast 机制是一个有挑战性的问题。先看看 Spark 官网上的一段话:
Broadcast variables allow the programmer to keep a read-only variable cached on each machinerather than shipping a copy of it with tasks. They can be used, for example, to give every node a copy of a large input dataset in an efficient manner. Spark also attempts to distribute broadcast variables using efficient broadcast algorithms to reduce communication cost.
广播变量允许程序员在每台机器上保存一个只读变量,而不是随任务发送一个副本。 例如,可以使用它们以有效的方式为每个节点提供大型输入数据集的副本。 Spark还尝试使用高效的广播算法来分发广播变量,以降低通信成本。
问题:为什么只能 broadcast 只读的变量?
这就涉及一致性的问题,如果变量可以被更新,那么一旦变量被某个节点更新,其他节点要不要一块更新?如果多个节点同时在更新,更新顺序是什么?怎么做同步?还会涉及 fault-tolerance 的问题。为了避免维护数据一致性问题,Spark 目前只支持 broadcast 只读变量。
问题:broadcast 到节点而不是 broadcast 到每个 task?
因为每个 task 是一个线程,而且同在一个进程运行 tasks 都属于同一个 application。因此每个节点(executor)上放一份就可以被所有 task 共享。
问题: 具体怎么用 broadcast?
driver program 例子:
val data = List(1, 2, 3, 4, 5, 6)
val bdata = sc.broadcast(data)
val rdd = sc.parallelize(1 to 6, 2)
val observedSizes = rdd.map(_ => bdata.value.size)
driver 使用 sc.broadcast() 声明要 broadcast 的 data,bdata 的类型是 Broadcast。
当 rdd.transformation(func) 需要用 bdata 时,直接在 func 中调用,比如上面的例子中的 map() 就使用了 bdata.value.size。
//全部的ip映射规则
val ipRulesArrary = ipRulesRdd.collect()
//广播规则
val ipRulesBroadcast = sc.broadcast(ipRulesArrary)
//加载要处理的数据
val ipsRDD = sc.textFile("c://access_log").map(line => {
val fields = line.split("\\|")
fields(1)
})
val result = ipsRDD.map(ip => {
val ipNum = ip2Long(ip)
val index = binarySearch(ipRulesBroadcast.value, ipNum)
val info = ipRulesBroadcast.value(index)
info
})