理解Spark包中闭包

spark难解的地方之一就是明白在集群上运行代码时,明白变量和函数的作用范围和生命周期,在作用域外修改变量的RDD操作很多时候会产生麻烦。接下来展示的例子我们会使用foreach() 来增加变量counter,相类似的问题也可能同样出现在其他loop问题中.

例子:

对于RDD中元素(element)求和。即使使用相同的JVM代码,由于不同的操作环境,操作的结果也可能不同,常用的例子是用Spark 在当地环境跑(--master = local[n])和在集群上(spark-submmit to YARN)

var counter = 0
var rdd = sc.parallelize(data)

// Wrong: Don't do this!!
rdd.foreach(x => counter += x)

println("Counter value: " + counter)
上面这段代码的结果是不明确和稳定的,可能不符合我们的预期.为了运行任务(jobs),Spark会将RDD操作过程分解成tasks,每个task由一个执行器执行。不过在执行前,Spark会计算所有task的闭包,闭包中包含了对于每个执行器可见的变量和函数(在这里是foreach()),闭包被序列化后发送给每个执行器.

闭包里面的变量被复制发送到每个执行器中,在foreach()函数中引用的counter变量不再是驱动节点(driver node)上的counter变量,当然在驱动节点的内存上还是存在counter变量,不过这个counter变量对于每个工作的执行器来说是不可见的,执行器只可以看见来自序列化闭包的复制变量,由于在counter变量上所做的所有操作都是引用的序列化闭包中数据,因此,counter最后的值还是为0。

在当地模式中,在某些环境下,foreach()函数将会使用与驱动节点相同的JVM来运行,将会使用相同原始变量counter,所以可以进行更新.

为了保证我们累加的结果是明确的,我们应该使用Accumulator.Spark中的Accumulator特别适合处理集群计算环境下(运行分解为多个worker nodes)对变量进行更新。

比如像 loops 或者 局部定义方法(locally defined methods)这种闭包结构,不应该用来改变全局变量,在这个例子中,我们使用foreach(x => +=x)来迭代增加全局变量。

 Spark不能保证闭包外对象的变动,比如在我们这个例子中闭包中的变量counter发生改动,在闭包外引用这个变量,其结果不会发生改变。可能有些类似的代码在本地会有效,不过这仅仅是侥幸,在分布式运行中,将不会达到我们的预期。对于全局变量累计的问题,应该转而使用Accumularor。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值