做了一年延云YDB的开发,这一年在使用spark上真心踩了不少坑,总结一下,希望对大家有所帮助。

spark 内存泄露

1.高并发情况下的内存泄露的具体表现

很遗憾,spark的设计架构并不是为了高并发请求而设计的,我们尝试在网络条件不好的集群下,进行100并发的查询,在压测3天后发现了内存泄露。

a)在进行大量小SQL的压测过程中发现,有大量的activejob在spark ui上一直处于pending状态,且永远不结束,如下图所示

b)并且发现driver内存爆满

c)用内存分析分析工具分析了下

2.高并发下AsynchronousListenerBus引起的WEB UI的内存泄露

短时间内 SPARK 提交大量的SQL ,而且SQL里面存在大量的 union与join的情形,会创建大量的event对象,使得这里的 event数量超过10000个event ,

一旦超过10000个event就开始丢弃 event,而这个event是用来回收 资源的,丢弃了 资源就无法回收了。 针对UI页面的这个问题,我们将这个队列长度的限制给取消了。

3.AsynchronousListenerBus本身引起的内存泄露

抓包发现

这些event是通过post方法传递的,并写入到队列里

但是也是由一个单线程进行postToAll的

但是在高并发情况下,单线程的postToAll的速度没有post的速度快,会导致队列堆积的event越来越多,如果是持续性的高并发的SQL查询,这里就会导致内存泄露

接下来我们在分析下postToAll的方法里面,那个路径是最慢的,导致事件处理最慢的逻辑是那个?

可能您都不敢相信,通过jstack抓取分析,程序大部分时间都阻塞在记录日志上

可以通过禁用这个地方的log来提升event的速度

log4j.logger.org.apache.spark.scheduler=ERROR

4.高并发下的Cleaner的内存泄露

说道这里,Cleaner的设计应该算是spark最糟糕的设计。spark的ContextCleaner是用于回收与清理已经完成了的 广播boradcast,shuffle数据的。但是高并发下,我们发现这个地方积累的数据会越来越多,最终导致driver内存跑满而挂掉。

l我们先看下,是如何触发内存回收的

没错,就是通过System.gc() 回收的内存,如果我们在jvm里配置了禁止执行System.gc,这个逻辑就等于废掉(而且有很多jvm的优化参数一般都推荐配置禁止system.gc 参数)

lclean过程

这是一个单线程的逻辑,而且每次清理都要协同很多机器一同清理,清理速度相对来说比较慢,但是SQL并发很大的时候,产生速度超过了清理速度,整个driver就会发生内存泄露。而且brocadcast如果占用内存太多,也会使用非常多的本地磁盘小文件,我们在测试中发现,高持续性并发的情况下本地磁盘用于存储blockmanager的目录占据了我们60%的存储空间。

我们再来分析下 clean里面,那个逻辑最慢

真正的瓶颈在于blockManagerMaster里面的removeBroadcast,因为这部分逻辑是需要跨越多台机器的。

针对这种问题,

l我们在SQL层加了一个SQLWAITING逻辑,判断了堆积长度,如果堆积长度超过了我们的设定值,我们这里将阻塞新的SQL的执行。堆积长度可以通过更改conf目录下的ya100_env_default.sh中的ydb.sql.waiting.queue.size的值来设置。

l建议集群的带宽要大一些,万兆网络肯定会比千兆网络的清理速度快很多。

l给集群休息的机会,不要一直持续性的高并发,让集群有间断的机会。

l增大spark的线程池,可以调节conf下的spark-defaults.conf的如下值来改善。

5.线程池与threadlocal引起的内存泄露

发现spark,hive,lucene都非常钟爱使用threadlocal来管理临时的session对象,期待SQL执行完毕后这些对象能够自动释放,但是与此同时spark又使用了线程池,线程池里的线程一直不结束,这些资源一直就不释放,时间久了内存就堆积起来了。

针对这个问题,延云修改了spark关键线程池的实现,更改为每1个小时,强制更换线程池为新的线程池,旧的线程数能够自动释放。

6.文件泄露

您会发现,随着请求的session变多,spark会在hdfs和本地磁盘创建海量的磁盘目录,最终会因为本地磁盘与hdfs上的目录过多,而导致文件系统和整个文件系统瘫痪。在YDB里面我们针对这种情况也做了处理。

7.deleteONExit内存泄露

为什么会有这些对象在里面,我们看下源码

8.JDO内存泄露

多达10万多个JDOPersistenceManager

9.listerner内存泄露

通过debug工具监控发现,spark的listerner随着时间的积累,通知(post)速度运来越慢

发现所有代码都卡在了onpostevent上

jstack的结果如下

研究下了调用逻辑如下,发现是循环调用listerners,而且listerner都是空执行才会产生上面的jstack截图

通过内存发现有30多万个linterner在里面

发现都是大多数都是同一个listener,我们核对下该处源码

最终定位问题

确系是这个地方的BUG ,每次创建JDBC连接的时候 ,spark就会增加一个listener, 时间久了,listener就会积累越来越多  针对这个问题 我简单的修改了一行代码,开始进入下一轮的压测

二十二、spark源码调优

测试发现,即使只有1条记录,使用 spark进行一次SQL查询也会耗时1秒,对很多即席查询来说1秒的等待,对用户体验非常不友好。针对这个问题,我们在spark与hive的细节代码上进行了局部调优,调优后,响应时间由原先的1秒缩减到现在的200~300毫秒。

以下是我们改动过的地方

1.SessionState 的创建目录 占用较多的时间

另外使用Hadoop namenode HA的同学会注意到,如果第一个namenode是standby状态,这个地方会更慢,就不止一秒,所以除了改动源码外,如果使用namenode ha的同学一定要注意,将active状态的node一定要放在前面。

2.HiveConf的初始化过程占用太多时间

频繁的hiveConf初始化,需要读取core-default.xml,hdfs-default.xml,yarn-default.xml

,mapreduce-default.xml,hive-default.xml等多个xml文件,而这些xml文件都是内嵌在jar包内的。

第一,解压这些jar包需要耗费较多的时间,第二每次都对这些xml文件解析也耗费时间。

3.广播broadcast传递的hadoop configuration序列化很耗时

lconfiguration的序列化,采用了压缩的方式进行序列化,有全局锁的问题

lconfiguration每次序列化,传递了太多了没用的配置项了,1000多个配置项,占用60多Kb。我们剔除了不是必须传输的配置项后,缩减到44个配置项,2kb的大小。

4.对spark广播数据broadcast的Cleaner的改进

由于SPARK-3015 的BUG,spark的cleaner 目前为单线程回收模式。

大家留意spark源码注释

其中的单线程瓶颈点在于广播数据的cleaner,由于要跨越很多台机器,需要通过akka进行网络交互。

如果回收并发特别大,SPARK-3015 的bug报告会出现网络拥堵,导致大量的 timeout出现。

为什么回收量特变大呢? 其实是因为cleaner 本质是通过system.gc(),定期执行的,默认积累30分钟或者进行了gc后才触发cleaner,这样就会导致瞬间,大量的akka并发执行,集中释放,网络不瞬间瘫痪才不怪呢。

但是单线程回收意味着回收速度恒定,如果查询并发很大,回收速度跟不上cleaner的速度,会导致cleaner积累很多,会导致进程OOM(YDB做了修改,会限制前台查询的并发)。

不论是OOM还是限制并发都不是我们希望看到的,所以针对高并发情况下,这种单线程的回收速度是满足不了高并发的需求的。

对于官方的这样的做法,我们表示并不是一个完美的cleaner方案。并发回收一定要支持,只要解决akka的timeout问题即可。
所以这个问题要仔细分析一下,akka为什么会timeout,是因为cleaner占据了太多的资源,那么我们是否可以控制下cleaner的并发呢?比如说使用4个并发,而不是默认将全部的并发线程都给占满呢?这样及解决了cleaner的回收速度,也解决了akka的问题不是更好么?

针对这个问题,我们最终还是选择了修改spark的ContextCleaner对象,将广播数据的回收 改成多线程的方式,但现在了线程的并发数量,从而解决了该问题。

那些年我们在spark SQL上踩过的坑相关推荐

  1. 字节跳动在 Spark SQL 上的核心优化实践

    作者 | 郭俊 封图 | BanburyTang 字节跳动数据仓库架构团队负责数据仓库领域架构设计,支持字节跳动几乎所有产品线(包含但不限于抖音.今日头条.西瓜视频.火山视频)数据仓库方向的需求,如 ...

  2. 字节跳动在Spark SQL上的核心优化实践

    字节跳动在Spark SQL上的核心优化实践 大数据架构 今天 以下文章来源于字节跳动技术团队 ,作者郭俊 字节跳动技术团队 字节跳动的技术实践分享 10月26日,字节跳动技术沙龙 | 大数据架构专场 ...

  3. 上海沙龙回顾 | ​字节跳动在Spark SQL上的核心优化实践

    10月26日,字节跳动技术沙龙 | 大数据架构专场 在上海字节跳动总部圆满结束.我们邀请到字节跳动数据仓库架构负责人郭俊,Kyligence 大数据研发工程师陶加涛,字节跳动存储工程师徐明敏,阿里云高 ...

  4. 我在Windows系统搭建python的Hadoop+Spark环境时踩过的坑

    本人是一个最近正在研究链路预测的小白,读论文的时候有幸从导师那里获得了论文相关的算法代码,可是论文里面涉及到了spark和Hadoop,需要搭建环境,于是这只无脑的小白成功踏入了搭建Hadoop+sp ...

  5. 那些年,在nodejs上踩过的坑

    原文:http://cnodejs.org/topic/4fc7789a8be5d070121141cd ----------------------------------------------- ...

  6. sql获取某列出现频次最多的值_那些SQL里面踩过的坑

    点击关注上方"SQL数据库开发", 设为"置顶或星标",第一时间送达干货 1. 关于空值 有如下一张表 当我们输入如下查询语句时: SELECT * FROM ...

  7. 搭建风控系统道路上踩过的坑合集

    作者前言 从业近10年,大大小小参与了3家公司不同领域的风控系统的设计,从前到后把风控系统所有环节都细细的琢磨过,然而至今仍然感觉刚刚一只脚踏进门而已. 大多数人做的产品都是目的明确的,比如订单支付. ...

  8. flutter ios上踩的一个坑

    在ios模拟器上调试的时候出现错误信息:The iOS Simulator deployment target is set to 4.3... 原来是因为ios 版本问题,于是找到资料是在 Podf ...

  9. 在MongoDB的MapReduce上踩过的坑

    太久没动这里,目前人生处于一个新的开始.这次博客的内容很久前就想更新上来,但是一直没找到合适的时间点(哈哈,其实就是懒),主要内容集中在使用Mongodb时的一些隐蔽的MapReduce问题: 1.R ...

最新文章

  1. 华宇输入法linux,华宇拼音输入法DEB版能切换为五笔输入法,附操作方法
  2. C++中全排列算法函数next_permutation的使用方法
  3. navicat for mysql 导入psc文件
  4. Gson案例:Java对象与JSON字符串相互转换
  5. NOIP2018游记题解
  6. 如何把Sql Server2005 数据库转换成Access
  7. C# 时时监听目录文件改动
  8. 微服务 雪崩效应 与 Hystrix 断路器使用入门
  9. 科学问题表述是机理与机制的区别
  10. 线性代数及其应用(英文第四版)下载
  11. 【Android】小米开发应用App上线注册
  12. XTDrone--执行roslaunch px4 indoor1.launch 遇到的问题
  13. Chrome 咕咕牛懒器(guge niú lǎn qì) 安装 及助手插件操作
  14. 5月语言排行榜:R 跌出前二十,Python 紧咬 C++
  15. PTA(每日一题)7-59 武林盟主
  16. C++并发编程(C++11到C++17)
  17. 什么是多态?实现多态的机制是什么?
  18. 数字图像处理——图像退化(大气湍流模型与运动模糊模型)与图像复原(逆滤波与维纳滤波)
  19. ssm框架搭建过程中的bug(石乐志)
  20. python实现画图工具

热门文章

  1. 移动安全框架(MobSF)
  2. Android电源管理介绍
  3. 储存器RAM、Flash、ROM、HHD简明对比
  4. 网页变灰的方法,适用于IE
  5. Android高工:细说 Android 多线程,探究原理知其所以然
  6. 人脸识别技术开发解决方案,人脸识别智慧校园应用开发
  7. SQL专项复习(使用GROUP BY, WITH AS)——习题篇02
  8. 实现海康监控视频播放(录像回放)(抓拍,录像等功能)
  9. 博文视点2008站长大会之行
  10. 全力加速,绝对实力!从Q3财报看华米科技的逆势而为