闭包引用是编程语言中的一个概念,指的是在一个函数中引用了函数外的变量。在Spark中,闭包引用是指分布式算子中引用外部变量的情况。普通的变量是在Driver程序中创建的,而RDD的计算是在分布式集群中的task程序上进行的。因此,当分布式算子的函数引用了外部的变量时,Driver会将该变量序列化后通过网络发送给每一个task(只针对普通对象)。
在闭包函数内对外部变量进行修改,并不会改变闭包外的变量。因为在闭包函数内的修改只是在task上对复制过来的闭包对象副本的修改,并不会影响到driver端的原对象,它们是存在于两台机器上的独立的文件,互不影响。
好处
闭包引用适用于大表join小表的场景,可以将小表数据(如字典表、维度表)发送到大表所在节点的内存中,在map阶段完成join,无需通过两个RDD去join,从而省去了shuffle操作。
适用条件
闭包引用的对象必须实现序列化接口(extends Serializable)。被引用的对象数据量不能太大,否则会导致task端内存溢出。
单例对象的闭包引用
单例对象(object对象)只在每个executor进程中持有一份,由其中的多个task线程共享。不要在task中对单例对象进行修改操作,否则会产生线程安全问题。而普通对象在每个task线程中都持有一份,不存在线程安全问题。
广播变量
在闭包引用的场景中,Spark为每个task都复制了一份它需要的数据。当数据量较大且task较多时,必然会给网络IO和内存资源造成很大压力。而广播变量只给每个executor发送一份变量副本,由多个task共享。
和闭包引用的区别
1、闭包引用中,是由driver给每个executor直接发送数据。广播变量中,是通过BitTorrent协议来发送数据的,可以减少通信成本(所有executor遵循了人人为我,我为人人的原则)。
2、闭包引用中,每个task持有一份引用对象的副本,并可以修改(只在该task线程生效);广播变量中,一个executor只持有一份广播变量的副本,由多个task共享,该数据是只读的,不可修改。