通常在使用Spark算子函数,比如使用map()或者reduce函数我们向函数传入条件时,函数内部可以使用驱动程序中定义的变量,但是这样会使集群中所有任务都会得到变量新的副本,这些副本的更新不会传播回驱动程序,导致读写共享变量效率低下或者内存溢出,为了解决这个问题Spark提供了两种共享变量类型:广播变量和累加器
- 广播变量:用来高效分发较大对象,只能在Driver定义,不能在Executor端定义,同时RDD不存储数据所以不能广播出去
- 累加器:用来对信息进行聚合,常用场景reduce()
不使用广播变量,直接定义一个变量list,然后在filter()来判断元素是否存在list中,实现代码如下:
from pyspark import SparkContext
if __name__ == '__main__':
sc = SparkContext(appName="broadcast", master="local[*]")
list = [2, 4, 6, 8]
data = [1, 2, 3, 4, 5]
rdd = sc.parallelize(data)
print(rdd.filter(lambda x: list.__contains__(x)).collect())
# [2, 4]
sc.stop()
定义的list变量在驱动端Driver创建的,但是要到Executor端运行,Driver端会把list以task形式拷贝到Executor端,如果有很多task那么就会有很多list复制过去,这个list非常大的时候就会造成内存溢出,关系图如下所示:
使用广播变量,变量只会被发送到各节点一次,同时存放在Executor的BlockManager中,实现代码如下:
from pyspark import SparkContext
if __name__ == '__main__':
sc = SparkContext(appName="broadcast", master="local[*]")
list = [2, 4, 6, 8]
bclist = sc.broadcast(list)
data = [1, 2, 3]
rdd = sc.parallelize(data)
print(rdd.map(lambda x: bclist.value[x]).collect())
# [4, 6, 8]
sc.stop()
关系图如下:
累加器,对作业执行过程事件进行计数,实现代码如下:
from pyspark import SparkContext
if __name__ == '__main__':
sc = SparkContext(appName="broadcast", master="local[*]")
data = [1, 2, 3]
rdd = sc.parallelize(data)
accumulator = sc.accumulator(0)
rdd.map(lambda x: accumulator.add(1)).collect()
print(accumulator.value)
# 3
sc.stop()
关系图如下:
累加器在Driver端定义赋初始值,在Executor端更新,最后在Driver端读取最后的值。
Spark学习目录:
- Spark学习实例1(Python):单词统计 Word Count
- Spark学习实例2(Python):加载数据源Load Data Source
- Spark学习实例3(Python):保存数据Save Data
- Spark学习实例4(Python):RDD转换 Transformations
- Spark学习实例5(Python):RDD执行 Actions
- Spark学习实例6(Python):共享变量Shared Variables
- Spark学习实例7(Python):RDD、DataFrame、DataSet相互转换
- Spark学习实例8(Python):输入源实时处理 Input Sources Streaming
- Spark学习实例9(Python):窗口操作 Window Operations