spark 使用的架构是无共享的,数据分布在不同节点,每个节点有独立的 CPU、内存,不存在全局的内存使得变量能够共享,驱动程序和任务之间通过消息共享数据

举例来说,如果一个 RDD 操作使用了驱动程序中的变量,spark 会将这个变量的副本和 task 一起发送给 executor 中的执行者,对该变量的更新只存在于 task 的内部,并不会回传给驱动程序;

如果这个任务分为多个阶段,每个阶段开始时,驱动程序会把 变量 发送给 worker;

在实际场景中,驱动程序在 task 间共享一个巨大(如 100M)的查找表,并且该 task 有多个(如 10个)阶段,spark 会在每个阶段开始时,给每个 task 发送一份数据,那就是 1000M 的传输和存储

显然浪费资源且效率低下;

除此之外,有时我们需要不同节点的多个任务更新一个全局变量,然而这个变量并不回传给驱动程序,又如何同步更新呢?

共享变量

spark 提供了共享变量的概率满足上述需求

spark 提供了两种共享变量:广播变量 和 累加器

上图描述了共享变量的工作原理

共享变量的作用在于 减少网络传输,减少内存消耗;

其实是一种 spark 编程的优化方法

spark 三大数据结构

RDD:分布式数据集

广播变量:分布式只读共享变量

累加器:分布式只写共享变量

广播变量

spark 只会给 worker 发送一次广播变量(序列化的),并且将它反序列化后存储在 executor 的内存中;

如果任务分为多个阶段,且每个阶段使用相同的变量,那么广播变量就无需每个阶段都传输数据,spark 会将传输过来的数据序列化后存储在 executor 的内存中,在任务开始前反序列化广播变量即可;

使用场景

1. 需要共享一个很大的数据集

2. 任务分为多个阶段,每个阶段使用相同的数据

使用方法

SparkContext.broadcast 方法用于创建一个广播变量;

broadcast 输入一个普通变量,返回一个 Broadcast 实例;

调用 Broadcast 实例的 value 属性可以获取变量值;

实例分析

import time
from pyspark import SparkContext
factor1 = ['sssssssssssssssssss']*100000 # 待广播的变量
factor2 = range(100000) if __name__ == '__main__':
time.clock()
sc = SparkContext() listRdd = sc.parallelize(factor2) ### 正确利用广播变量 # 测试:1.40s
brodacastvalue = sc.broadcast(factor1)
out = listRdd.map(lambda s: brodacastvalue.value[s-1]).collect() ### 不正确利用广播变量 # 测试:1.59s
brodacastvalue = sc.broadcast(factor1).value
out = listRdd.map(lambda s: brodacastvalue[s-1]).collect() # 这里注意了,并没有调用 广播变量,而是调用了它的 value,相当于直接调用了原来的数据 ### 不利用广播变量 # 测试:1.59s
out = listRdd.map(lambda s: factor1[s-1]).collect() print out
print(time.clock())

可以看到广播变量优化了效率,特别是替代 join 操作

累加器

累加器使得多个任务能够操作同一个全局变量, 并且能够回传给驱动程序;

它常用于计数、求和和聚合操作;

spark 支持数值型的累加,也支持自定义类型的累加;

使用方法

SparkContext.accumulator 创建一个累加器变量;

它接受两个参数:累加器的初值,累加器的名字,在 spark UI 中可看到,第二个参数可选

在 task 中只能使用累加器变量的 add 属性或者 += 来更新该变量;

在 驱动程序 中才能调用累加器变量的 value 属性,在 task 中不可以;

实例

from pyspark import SparkContext

sc = SparkContext(appName='leijiaqi')
accum = sc.accumulator(10) # 初值,名字
# sc.parallelize([1, 2, 3, 4]).foreach(lambda x : accum.add(x)) # 调用 add 属性更新变量 def myadd(x):
global accum
accum+=x
return accum
sc.parallelize([1, 2, 3, 4]).foreach(myadd) # 使用 += 方法更新变量
print(accum.value) # 20 = 10+1+2+3+4 # sc.parallelize([1, 2, 3, 4]).foreach(lambda x : accum.value) # 在 task 中调用 value 报错:Exception: Accumulator.value cannot be accessed inside tasks

总结

累加器和广播变量使用非常复杂,本文仅介绍基本用法,二者结合使用可以满足很多复杂场景

参考资料:

https://blog.csdn.net/chongxin1/article/details/78048134

https://www.jianshu.com/p/687db128ff2f  python 版

spark教程(14)-共享变量的更多相关文章

  1. Spark教程——(11)Spark程序local模式执行、cluster模式执行以及Oozie/Hue执行的设置方式

    本地执行Spark SQL程序: package com.fc //import common.util.{phoenixConnectMode, timeUtil} import org.apach ...

  2. [SQL基础教程]1-4 SQL 表的创建

    [SQL基础教程]1-4 SQL 表的创建 创建数据库 语法 CREATE DATABASE <数据库名称> // example CREATE DATABASE shop; 创建表 语法 ...

  3. spark教程

    某大神总结的spark教程, 地址 http://litaotao.github.io/introduction-to-spark?s=inner

  4. [译]Vulkan教程(14)图形管道基础之固定功能

    [译]Vulkan教程(14)图形管道基础之固定功能 Fixed functions 固定功能 The older graphics APIs provided default state for m ...

  5. [转帖]Linux教程(14)- Linux中的查找和替换

    Linux教程(14)- Linux中的查找和替换 2018-08-22 07:03:58 钱婷婷 阅读数 46更多 分类专栏: Linux教程与操作 Linux教程与使用   版权声明:本文为博主原 ...

  6. Directx11教程(14) D3D11管线(3)

    原文:Directx11教程(14) D3D11管线(3)       现在我们开始学习一些CP(command processor)的知识.参考资料: http://fgiesen.wordpres ...

  7. 深度学习与CV教程(14) | 图像分割 (FCN,SegNet,U-Net,PSPNet,DeepLab,RefineNet)

    作者:韩信子@ShowMeAI 教程地址:http://www.showmeai.tech/tutorials/37 本文地址:http://www.showmeai.tech/article-det ...

  8. spark教程(八)-SparkSession

    spark 有三大引擎,spark core.sparkSQL.sparkStreaming, spark core 的关键抽象是 SparkContext.RDD: SparkSQL 的关键抽象是 ...

  9. spark教程(九)-操作数据库

    数据库也是 spark 数据源创建 df 的一种方式,因为比较重要,所以单独算一节. 本文以 postgres 为例 安装 JDBC 首先需要 安装 postgres 的客户端驱动,即 JDBC 驱动 ...

随机推荐

  1. CF1216E Numerical Sequence

    题目链接 问题分析 奇奇怪怪的题... 首先思路达成一致,从大到小一步一步确定位置. 我们一边分析,一边讲算法. 1121231234123451234561234567123456781234567 ...

  2. Java基础__Integer类型中的自动装箱

    Integer类型的自动装箱:就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱. ...

  3. python中的fstring的 !r,!a,!s

    首先是fstring的结构 f ' <text> { <expression> <optional !s, !r, or !a> <optional : fo ...

  4. SpringBoot2.X 静态文件配置

    Spring Boot 默认会挨个从 META/resources > resources > static > public 里面找是否存在相应的资源,如果有则直接返回. 默认配置 ...

  5. vue使用laydate.js插件报错laydate.css: Invalid

    在vue中使用laydate.js插件时可能会碰到laydate.css: Invalid这样子的一个报错 然后导致laydate日期控件无法使用. 这主要是因为laydate.js中引入的layda ...

  6. linux中的i2c控制器驱动应该做些什么?

    答:完成读写功能即可,不要涉及任何针对从设备的特殊操作,这样就可以避免与从设备的代码产生冗余,linux中的i2c控制器驱动就做的特别棒,做好最简单的读写,然后其它的从设备驱动就是直接调用这些最简单的 ...

  7. LC 535. Encode and Decode TinyURL

    Note: This is a companion problem to the System Design problem: Design TinyURL. TinyURL is a URL sho ...

  8. LC 465. Optimal Account Balancing 【lock,hard】

    A group of friends went on holiday and sometimes lent each other money. For example, Alice paid for ...

  9. php怎样获取当前页面文件名

    因开发需要,常需要获取当前php文件的文件名.php获取当前文件名方法很简单,程序代码如下: <?php function php_self(){ $php_self=substr($_SERV ...

  10. linux/windows/Mac平台生成随机数的不同方法

    linux平台,使用rand.Seed() //rand_linux.go package main import ( "math/rand" "time" ) ...