spark 之所以需要调优,一是代码执行效率低,二是经常 OOM

内存溢出

内存溢出无非两点

1. Driver 内存不够

2. Executor 内存不够

Driver 内存不够无非两点

1. 读取数据太大

2. 数据回传

Executor 内存不够无非两点

1. map 类操作产生大量数据,包括 map、flatMap、filter、mapPartitions 等

2. shuffle 后产生数据倾斜

Executor 内存不够

有个通用的解决办法就是增加 Executor 内存

--executor-memory MEM       Memory per executor (e.g. 1000M, 2G) (Default: 1G).

但这并不一定是最好的办法

map 过程产生大量对象

造成 Executor 内存溢出

解决思路是减少每个 task 的大小,从而减少每个 task 的输出;

具体做法是在 会产生大量对象的 map 操作前 添加 repartition(重新分区) 方法,分区成更小的块传入 map

rdd.flatMap(lambda x: ['%d'%x*50 for _ in range(100000000)]).count()      # 100 * 100000000 个对象,内存溢出
rdd.flatMap(lambda x: len(['%d'%x*50 for _ in range(100000000)])).sum() # 内存溢出 rdd.repartition(1000000).flatMap(lambda x: ['%d'%x*50 for _ in range(100000000)]).count() # 可执行

数据倾斜

参考我的博客 数据倾斜

standalone 模式资源分配不均

该模式下配置了

--total-executor-cores NUM  (Total cores for all executors.)   集群 executor 核数

--executor-memory MEM       Memory per executor (e.g. 1000M, 2G) (Default: 1G).  每个 executor 内存

而没有配置

--executor-cores NUM Number of cores per executor. (Default: 1 in YARN mode,
or all available cores on the worker in standalone mode)  每个 executor 核数

假如各个 executor 核数不一样,核数多的 executor 执行的 task 就多,内存就容易溢出

解决方法是配置参数 --executor-cores,或者是在 spark 中配置 spark.executor.cores

在 RDD 中共用对象

rdd = sc.parallelize(range(100))
def myfunc(x): return x
rdd.flatMap(lambda x: [('k', 'v') for _ in range(200000000)]).foreach(myfunc) # 每次生成一个 tuple 对象,内存溢出
rdd.flatMap(lambda x: ['k'+'v' for _ in range(2000000)]).count() # 无需生成新的 string 对象,可执行

tuple 为不可变对象,不过字符串也是可变对象

此条方法有待进一步验证

Driver 中需要读取大量数据

造成 Driver 内存溢出

解决思路是增加 Driver 内存,具体做法为设置参数

--driver-memory MEM         Memory for driver (e.g. 1000M, 2G) (Default: 1024M). 

示例

from pyspark import SparkContext
sc = SparkContext(master='yarn')
rdd = sc.parallelize(range(300000000))
# spark-submit --master yarn-client --driver-memory 512M driver_oom.py 内存溢出
# spark-submit --master yarn-client --driver-memory 3G driver_oom.py 可以执行

collect

大量数据回传 Driver,造成内存溢出

解决思路是分区输出,具体做法是 foreach

rdd = sc.parallelize(range(100))
rdd.flatMap(lambda x: ['%d'%x*50 for _ in range(100000)]).collect() # 内存溢出 def func(x): print(x)
rdd.flatMap(lambda x: ['%d'%x*50 for _ in range(100000)]).foreach(func) # 分区输出

或者增加 Driver 内存

代码优化

mapPartitions

1. 批处理

2. 减少中间输出

用 mapPartitions 替代多个 map,减少 Executor 内存压力

from pyspark import SparkContext
sc = SparkContext(master='yarn')
data = range(10)
rdd = sc.parallelize(data, 2) ##### map
rdd.map(lambda x: x % 3).filter(lambda x : x>1 ).countByValue().values() # [3] ##### mapPartitions
# 避免了中间 RDD 的产生,节约内存,防止 oom
def myfunc(datas):
# datas type is itertools.chain
for data in datas:
value = data % 3
if value > 1:
yield value print rdd.mapPartitions(myfunc).countByValue().values() # [3]
# spark-submit --master yarn-client mapVSmapPartitions.py python 只支持 client 模式

DataFrame 代替 RDD

任务被划分成多个 stage,在每个 stage 内部,RDD 是无法自动优化的,

rdd.map(lambda x: x+1).map(lambda x: x+1)   ==  rdd.map(lambda x: x+2)

如上面两个操作是等价的,但是 RDD 并不会自动优化,

而 DataFrame 使用 sql 查询,自带 sql 优化器,可自动找到最优方案

broadcast join

在分布式计算中,数据跨节点移动是非常影响性能的,网络传输耗时,多次传输消耗内存,broadcast 在某些场景可以减少数据移动;

如 一个 小RDD 要和 一个 大RDD 进行 join 操作,常规情况下要互传 RDD,由于多个 task,故需多次传输,    【注意必须是有个小 RDD,否则这种做法意义不大,因为后面要遍历这个广播变量】

如果把 小RDD 变成 broadcast 变量,就不用传输 大RDD,把 broadcast(小RDD) 缓存在对应 Executor 上即可

对 大RDD 进行 map 操作,在 map 函数中调用 小RDD 的 value,遍历 小RDD

map(lambda x: i for i in smallRDD.value if x == i)

filter 之后再 join

就是所谓的谓词下推,在 sparkSQL 中会自动这么操作;

如果是自己操作 RDD,可以减少 shuffle 的数据量

cache and persist

缓存 RDD 既可以节省内存,也可以提高性能;

cahce 是缓存到内存,等同于 persist(Storage.MEMORY_ONLY),在内存不足时,这种缓存方式会丢失数据,再次使用时会重新计算;

rdd.persist(StorageLevel.MEMORY_AND_DISK_SER) 在内存不足时会写到磁盘,避免重复,只是耗费一点 IO 时间

combineByKey

在 hadoop 中也有 combine,有 combine 比 没有combine 效率高;

比如 reduceByKey (combine操作) 就比 groupyByKey (非combine操作) 效率高

import time
from pyspark import SparkContext sc = SparkContext(master='yarn') strs = list('abcd')*10000000
rdd = sc.parallelize(strs) time.clock()
print rdd.map(lambda x: (x, 1)).reduceByKey(lambda x, y: x+y).collect() # combinByKey 操作耗时少3.2s
# print rdd.map(lambda x: (x, 1)).groupByKey().mapValues(sum).collect() # 非 combinByKey 操作耗时3.6s
# 二者结果一样
print(time.clock()) strs = list('abcd')*10000000
for i in strs:i = (i, 1) # 6s,单机for循环做更少的事情,耗时更多

图解如下

参考资料:

https://blog.csdn.net/yhb315279058/article/details/51035631

spark调优篇-oom 优化(汇总)的更多相关文章

  1. spark调优篇-数据倾斜(汇总)

    数据倾斜 为什么会数据倾斜 spark 中的数据倾斜并不是说原始数据存在倾斜,原始数据都是一个一个的 block,大小都一样,不存在数据倾斜: 而是指 shuffle 过程中产生的数据倾斜,由于不同的 ...

  2. Spark调优,性能优化

    Spark调优,性能优化 1.使用reduceByKey/aggregateByKey替代groupByKey 2.使用mapPartitions替代普通map 3.使用foreachPartitio ...

  3. spark调优篇-Spark ON Yarn 内存管理(汇总)

    本文旨在解析 spark on Yarn 的内存管理,使得 spark 调优思路更加清晰 内存相关参数 spark 是基于内存的计算,spark 调优大部分是针对内存的,了解 spark 内存参数有也 ...

  4. spark调优篇-spark on yarn web UI

    spark on yarn 的执行过程在 yarn RM 上无法直接查看,即 http://192.168.10.10:8088,这对于调试程序很不方便,所以需要手动配置 配置方法 1. 配置 spa ...

  5. Linux网卡调优篇-禁用ipv6与优化socket缓冲区大小

    Linux网卡调优篇-禁用ipv6与优化socket缓冲区大小 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任.   一般在内网环境中,我们几乎是用不到IPV6,因此我们没有必要把多不 ...

  6. 性能优化 | JVM性能调优篇——来自阿里P7的经验总结

    VM 调优概述: 性能定义: 吞吐量 - 指不考虑 GC 引起的停顿时间或内存消耗,垃圾收集器能支撑应用达到的最高性能指标. 延迟 - 其度量标准是缩短由于垃圾啊收集引起的停顿时间或者完全消除因垃圾收 ...

  7. Linux虚拟内存(swap)调优篇-“swappiness”,“vm.dirty_background_ratio”和“vm.dirty_ratio”

      Linux虚拟内存(swap)调优篇-“swappiness”,“vm.dirty_background_ratio”和“vm.dirty_ratio” 作者:尹正杰 版权声明:原创作品,谢绝转载 ...

  8. 【Spark调优】Shuffle原理理解与参数调优

    [生产实践经验] 生产实践中的切身体会是:影响Spark性能的大BOSS就是shuffle,抓住并解决shuffle这个主要原因,事半功倍. [Shuffle原理学习笔记] 1.未经优化的HashSh ...

  9. Spark调优秘诀——超详细

    版权声明:本文为博主原创文章,转载请注明出处. Spark调优秘诀 1.诊断内存的消耗 在Spark应用程序中,内存都消耗在哪了? 1.每个Java对象都有一个包含该对象元数据的对象头,其大小是16个 ...

随机推荐

  1. elasticsearch shield在java中的应用

    官方文档:https://www.elastic.co/guide/en/shield/current/_using_elasticsearch_java_clients_with_shield.ht ...

  2. JAVA基础知识|TCP/IP协议

    虽然写代码也有一定的年头了,但是对于一些基础概念,还是很模糊.这在后来的学习过程中,带来了很大的痛苦,所以痛定思痛,决心重新学习这些概念.并把自己的理解和查询的资料做一些整合,便于以后查阅! 一.什么 ...

  3. golang中文件以及文件夹路径相关操作

    获取目录中所有文件使用包: io/ioutil 使用方法: ioutil.ReadDir 读取目录 dirmane 中的所有目录和文件(不包括子目录) 返回读取到的文件的信息列表和读取过程中遇到的任何 ...

  4. 本地项目文件通过git提交到GitHub上

    参考:https://blog.csdn.net/kongying19910218/article/details/50515834 步骤: 1.初始化git,假如我们要提交test文件夹下的所有目录 ...

  5. memcached出现:Fatal error: Call to undefined method Memcached::connect()

    今天安装了memcached的服务端和客户端 装好试了一下 $mem = new Memcached;  $mem -> connect("127.0.0.1",11211) ...

  6. Oracle常用操作表结构的语句

    首先,一起来认识几个单词. alter (改变) rename(重命名) column(柱子,用来表示列) modify(修改) comment on (评论) truncate (删减,截断) 1. ...

  7. win10设置开机开启数字锁定

    windows10开机小键盘默认关闭,网上查询修改InitialKeyboardIndicators键值为2,或者80000002,经过实际测试,均无效,键值8000000002有效,是中间8个0,开 ...

  8. vue-cli项目模板的一些思考

    之前有个想法,就是要利用vue写一套ui.然后当时也没有搞清楚到底怎么写. 几经周转吧,通过付费的方式在gitbook上面找到了答案. 找到答案之后再看我们正在开发的项目,看伙伴写的代码,突然发现完全 ...

  9. C#的String.Format举例

    1.格式化货币(跟系统的环境有关,中文系统默认格式化人民币,英文系统格式化美元) string.Format("{0:C}",0.2) 结果为:¥0.20 (英文操作系统结果:$0 ...

  10. oracle自增主键

    本文参考-https://www.cnblogs.com/xxaxx/p/3584036.html oracle没有像sqlserver中identity一样的函数,需要依赖于序列.触发器来实现自增主 ...