前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >从一个sql任务理解spark内存模型

从一个sql任务理解spark内存模型

作者头像
数据仓库践行者
发布2020-04-21 15:53:44
1.5K0
发布2020-04-21 15:53:44
举报

之前是只知道内存模型理论上是怎么样的,这次拿到一个具体的任务,具体的executor来做对照分析,加深理解,在调内存参数时,也能有个依据。

1、背景

下面是一个sql任务的executor界面:

该任务运行没有报oom,能正够正常执行完毕,但观察executor Summary页面,有大量executor GC时间过长(GC时长已经超过总任务时长的10%,一般GC时长建议控制在总任务时长的5%以内)。

2、分析

先给出相关的参数(目前所在平台默认参数):

  • spark.executor.memory=8G
  • spark.executor.memoryOverhead=6144(6G)
  • spark.memory.fraction=0.6

观察每个stage及job,并没有数据倾斜现象。

以某个Executor为例:

Dtop mem:

从图上可以看到,used_mem为8.96G 左右(最大为9.85G),alloc_mem:14.34G。

会觉得:哎呀,内存看上去还可以呀,挺充足呀,怎么就GC了呢?

实际上dtop页面的:

alloc_mem=executor memory(8G)+ memoryOverhead(6G)

used_mem=jvm实际使用量+overhead实际使用量

overhead我们管不着,那看看jvm heap的情况吧。

monitor 的jvm相关:

主要看三个参数:

  • max_heap:表示可用的最大内存
  • commited_heap: JVM 堆已 commit 的内存(包括实际分配的物理内存和未实际分配的内存) commited_heap <= max_heap ,当used_heap 接近committed_heap的时候,heap就会grow up,直到等于max_heap
  • used_heap: jvm中活动对象占用的内存,即实际的物理使用内存

commited_heap已经为8G,达到最大极限了。

used_heap为5G左右,整个过程中,最大的能达到6.89G。

这时候,会不会又觉得,最大8G,现在最多也才用6.89G,还有1G的内存没用啊?

回顾一下spark统一内存模型:

jvm堆内的内存分为四个部分(spark.memory.fraction=0.6):

  • reservedMemory:预留内存300M,用于保障spark正常运行
  • other memory:用于spark内部的一些元数据、用户的数据结构、防止出现对内存估计不足导致oom时的内存缓冲、占用空间比较大的记录做缓冲;估算大小为2.3G(8G-300M)*0.6*0.5
  • execution:用于spark的计算:shuffle、sort、aggregation等这些计算时会用到的内存;估算大小为2.3G(8G-300M)*0.6*0.5
  • storage:主要用于rdd的缓存;3G(8G-300M)*0.4

其中如果计算是内存execution不足会向storage部分借,如果还是不够就会spill到磁盘。

如果execution来借内存,storage会牺牲自己丢弃缓存来借给execution,storage也可以向execution借内存,但execution不会牺牲自己。

因此,我们可以认为计算内存execution 可用最大内存为4.6G

used_heap 包含了计算内存和 othermemory 、reservedmemory、storage 的真实使用量。

没办法看到othermemory部分的实际使用内存大小,但可以确定,富裕出来的1G左右的内存是othermemory 没有用完的并且计算内存execution一定是不太够用了,因为整个运行过程一直伴随着gc,并且gc的时间是越来越长:

最严重的是,在最后,老年代也发生了gc

3、总结

上面说了那么多,也有点乱,总结一下:

判断内存够不够,不能只看总图,要清楚内存分几个模块,几个模块分别起什么作用。一般出现内存不够用的地方是 shuffle时的计算内存,计算内存真实的可用内存大小并不是在dtop总图上看到的那么大。

如果spark.executor.memory=8G , 则计算内存可用最大为:4.6G

从上面分析,发现堆外内存堆最大使用量差不多2G,而默认的 spark.executor.memoryOverhead=6144(6G) 有点浪费

最后测试参数:

spark.executor.memory=12G

spark.executor.memoryOverhead=3072(3G)

set spark.memory.fraction=0.75

最合适

其中spark.memory.fraction 不能设置太高,测试时,要为othermemory留一些富裕内存,因为spark内存统计信息收集是有延迟的,如果该值过大,且spill较重情况下,会导致内存释放不及时而OOM。

内存参数该设置多少,没有确切计算方法,可以依据经验设定,然后多次测试出最合适的值。

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-04-20,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 数据仓库践行者 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档