本地模式与集群模式
以上代码的结果是不明确的,也可能无法预期地工作。要执行作业,Spark会打断RDD操作任务的处理,每个任务由一个执行器执行。在执行之前,Spark计算任务的闭包。闭包是那些变量和方法,对于执行器在RDD上执行计算时必须可见。这些闭包会序列化并发送到每一个执行器。
闭包被发送到每个执行器,闭包里的变量也因此被复制,当counter被foreach函数引用时,它不再是驱动节点的counter了。在驱动节点的内存里仍然有一个counter,但对执行器是不可见的。执行器只能看到复制过来的序列化的闭包。因此,counter最终的值将仍然是0,因为所有对counter的操作引用的是序列化的闭包里的值。
在本地模式,在一些循环的foreach函数中将执行相同的JVM作为驱动程序,将引用相同的原始的counter,且可能真的会更新它。
在这些场景中,为了保证能够达到预期效果,应该使用一个累加器(Accumulator)。在Spark中,累加器被专门用于,当执行跨越多个worker节点时,对安全地更新一个变量,提供了一个机制。指南的Accumulator章节有更详细的讨论。
通常,闭包构造,像循环或本地方法,不应該用于产生一些全局状态。Spark不能定义或保证对引用自闭包外部的对象的变化行为。在本地模式中,有些代码可能可以工作,但是那仅仅是偶然,这些代码将不能在分布式模式中得到预期的效果。如果需要一些全局聚合,使用累加器(Accumulator)来代替。