草庐IT

深入理解Spark原理,从性能优化入手

公众号JavaEdge 2023-03-28 原文

1 Spark任务文件初始化调优

首先进行性能测试,发现这个视频图谱N度级联关系应用分为5个job,最后一个job为保存结果到HDFS,其余job为同样计算过程的反复迭代。但是发现第一个job比其他job又多了个计算阶段stage,如图中红圈所示。

通过阅读程序代码,发现第一个job需要初始化一个空数组,从而产生了一个stage,但是这个stage在性能测试结果上显示,花费了14秒的时间,远远超出合理的预期范围。同时,发现这段时间网络通信也有一定开销,事实上只是内存数据初始化,代码上看不出需要进行网络通信的地方。下图是其中一台计算节点的通信开销,发现在第一个stage,写通信操作几乎没有,读通信操作大约每秒几十MB的传输速率。

分析Spark运行日志,发现这个stage主要花费时间并不是处理应用的计算逻辑,而是在从Driver进程下载应用执行代码。前面说过,Spark和MapReduce都是通过移动计算程序到数据所在的服务器节点,从而节省数据传输的网络通信开销,并进行分布式计算,即移动计算比移动数据更划算,而移动计算程序就是在这个阶段进行。

这个视频关系图谱计算程序因为依赖一个第三方的程序包,整个计算程序打包后大小超过17MB,这个17MB的JAR包需要部署到所有计算服务器上,即Worker节点上。但是只传输17MB的数据不可能花费这么多时间啊?
进一步分析Spark日志和代码后发现,每个计算节点上会启动多个Executor进程进行计算,而Spark的策略是每个Executor进程自己去下载应用程序JAR包,当时每台机器启动了30个Executor进程,这样就是4×30=120个进程下载,而Driver进程所在机器是一块千兆网卡,导致将这些数据传输完成花费了14秒的时间。
发现问题以后,解决办法就显而易见了。同一台服务器上的多个Executor进程不必每个都通过网络下载应用程序,只需要一个进程下载到本地后,其他进程将这个文件copy到自己的工作路径就可以了。

这段代码有个技术实现细节需要关注,就是多个进程同时去下载程序包的时候,如何保证只有一个进程去下载,而其他进程阻塞等待,也就是进程间的同步问题。
解决办法是使用了一个本地文件作为进程间同步的锁,只有获得文件锁的进程才去下载,其他进程得不到文件锁,就阻塞等待,阻塞结束后,检查本地程序文件是否已经生成。
这个优化实测效果良好,第一个stage从14秒下降到不足1秒,效果显著。

该案例的具体代码:https://github.com/apache/spark/pull/1616

Spark任务调度优化

将4台Worker机器CPU使用率进行对比:

在第一个job的第二个阶段,第三台机器的CPU使用率和其他机器明显不同,即计算资源利用不均衡:有忙有闲的资源分配方式通常会引发性能问题

分析Spark日志和源码,当有空闲计算资源的Worker节点向Driver注册时,就会触发Spark任务分配,而分配使用轮询,每个Worker都会轮流分配任务,保证任务分配均衡,每个服务器都能领到一部分任务。
但实测结果是在第二个stage,只有一个Worker服务器领了任务,而其他服务器无任务执行!

分析日志,发现Worker节点向Driver注册有先有后,先注册的Worker开始领取任务,若:
$需执行任务数<Worker提供的计算单元数$,就会导致一个Worker领走所有任务。
而第一个job的第二个stage就是该场景,demo数据量小,按HDFS默认的Block大小,只有17个Block,第二个stage就是加载这17个Block进行初始迭代计算,只需要17个计算任务就能完成,所以当第三台服务器先于其他三台服务器向Driver注册的时候,触发Driver的任务分配,领走所有17个任务。
同时,为避免这种一个Worker先注册导致先领走全部任务,考虑增加一个配置项,只有注册的计算资源数达到阈值比例才开始分配任务:

spark.scheduler.minRegisteredResourcesRatio = 0.8
为了避免注册计算资源达不到期望资源比例而无法开始分配任务,在启动任务执行时,又增加了一个配置项,也就是最小等待时间,超过最小等待时间(秒),不管是否达到注册比例,都开始分配任务。

spark.scheduler.maxRegisteredResourcesWaitingTime = 3
启用这两个配置项后,第二个stage的任务被均匀分配到4个Worker服务器上,执行时间缩短了1.32倍。而4台Worker服务器的CPU利用率也变得很均衡了。

这个案例的具体代码你可以参考:https://github.com/apache/spark/pull/900
https://github.com/apache/spark/pull/1525

3 Spark应用配置优化

看案例2的几张CPU利用率的图,我们还发现所有4个Worker服务器的CPU利用率最大只能达到60%多一点。例如下图,绿色部分就是CPU空闲。

这种资源利用瓶颈的分析无需分析Spark日志和源代码,根据Spark原理,稍加思考就发现,当时使用的这些服务器CPU48核,而应用配置最大Executor数目120,每台服务器30个任务,虽然30个任务在每个CPU核上都100%运行,但总CPU使用率仍只有60%。
优化也简单,设置应用启动参数的Executor数为48×4=192。

4 操作系统配置优化

性能测试时发现,当使用不同服务器时,CPU资源利用情况也不同,某些服务器CPU处于sys态,即系统态运行的占比高:

图中紫色为CPU处于sys态,某些时候sys态占了CPU总使用率的近80%,这个比例显然是不合理的,表示虽然CPU很忙,但是没有执行用户计算,而是在执行操作系统的计算。
那么,操作系统究竟在忙什么,占用了这么多CPU时间?通过跟踪Linux内核执行指令,发现这些sys态的执行指令和Linux的配置参数transparent huge pages有关。
当transparent huge pages打开的时候,sys态CPU消耗就会增加,而不同Linux版本的transparent huge pages默认是否打开是不同的,对于默认打开transparent huge pages的Linux执行下面的指令,关闭transparent huge pages。

echo never > /sys/kernel/mm/transparent_hugepage/enabled
echo never > /sys/kernel/mm/ transparent_hugepage/defrag
关闭以后,对比前面的CPU消耗,sys占比明显下降,总的应用耗时也有明显下降。

5 硬件优化

分析网卡资源消耗,发现网络通信是性能瓶颈,对整个应用的影响明显。
比如在第二个、第三个job,网络通信消耗长达50s,网络读写通信都达到了网卡的最大吞吐能力,整个集群都在等待网络传输。

千兆网卡最大传输速率是125MB/s,这样的速率和CPU内存肯定没法比,而虽然比单个磁盘快点,但服务器磁盘是8块磁盘组成的阵列,总磁盘吞吐量碾压千兆网卡,因此网卡传输速率瓶颈成为系统性能瓶颈。
优化很简单:使用万兆网卡。

以前需要50s网络通信时间,缩短为10s左右。从性能曲线上看,网络通信在刚刚触及网卡最大传输速率的时候,就完成传输,总计算时间缩短近100s。

总结

大数据软件性能优化会涉及硬件、操作系统、大数据产品及其配置、应用程序开发和部署几个方面。当性能不能满足需求的时候,先看看各项性能指标是否合理,如果资源没有全面利用,那么可能是配置不合理或者大数据应用程序(包括SQL语句)需要优化;如果某项资源利用已经达到极限,那么就要具体来分析,是集群资源不足,需要增加新的硬件服务器,还是需要对某项硬件、操作系统或是JVM,甚至是对大数据产品源代码进行调优。

有关深入理解Spark原理,从性能优化入手的更多相关文章

  1. CAN协议的学习与理解 - 2

    最近在学习CAN,记录一下,也供大家参考交流。推荐几个我觉得很好的CAN学习,本文也是在看了他们的好文之后做的笔记首先是瑞萨的CAN入门,真的通透;秀!靠这篇我竟然2天理解了CAN协议!实战STM32F4CAN!原文链接:https://blog.csdn.net/XiaoXiaoPengBo/article/details/116206252CAN详解(小白教程)原文链接:https://blog.csdn.net/xwwwj/article/details/105372234一篇易懂的CAN通讯协议指南1一篇易懂的CAN通讯协议指南1-知乎(zhihu.com)视频推荐CAN总线个人知识总

  2. TimeSformer:抛弃CNN的Transformer视频理解框架 - 2

    Transformers开始在视频识别领域的“猪突猛进”,各种改进和魔改层出不穷。由此作者将开启VideoTransformer系列的讲解,本篇主要介绍了FBAI团队的TimeSformer,这也是第一篇使用纯Transformer结构在视频识别上的文章。如果觉得有用,就请点赞、收藏、关注!paper:https://arxiv.org/abs/2102.05095code(offical):https://github.com/facebookresearch/TimeSformeraccept:ICML2021author:FacebookAI一、前言Transformers(VIT)在图

  3. ruby - 易于初学者理解的 Ruby 库 - 2

    关闭。这个问题不符合StackOverflowguidelines.它目前不接受答案。我们不允许提问寻求书籍、工具、软件库等的推荐。您可以编辑问题,以便用事实和引用来回答。关闭3年前。Improvethisquestion我正处于学习Ruby的阶段,我想查看一些小型库的源代码以了解它们是如何构建的。我不知道什么是小型图书馆,但希望SO能推荐一些易于理解的图书馆来学习。因此,如果有人知道一两个非常小的库,这是新手Rubyists学习的好例子,请推荐!我想使用Manveru'sInnatelib,因为它试图保持在2000LOC以下,但我还不熟悉其中经常使用的Ruby速记。也许大约100-5

  4. ruby - 无法理解 `puts{}.class` 和 `puts({}.class)` 之间的区别 - 2

    由于匿名block和散列block看起来大致相同。我正在玩它。我做了一些严肃的观察,如下所示:{}.class#=>Hash好的,这很酷。空block被视为Hash。print{}.class#=>NilClassputs{}.class#=>NilClass为什么上面的代码和NilClass一样,下面的代码又显示了Hash?puts({}.class)#Hash#=>nilprint({}.class)#Hash=>nil谁能帮我理解上面发生了什么?我完全不同意@Lindydancer的观点你如何解释下面几行:print{}.class#NilClassprint[].class#A

  5. Ruby 的数字方法性能 - 2

    我正在使用Ruby解决一些ProjectEuler问题,特别是这里我要讨论的问题25(Fibonacci数列中包含1000位数字的第一项的索引是多少?)。起初,我使用的是Ruby2.2.3,我将问题编码为:number=3a=1b=2whileb.to_s.length但后来我发现2.4.2版本有一个名为digits的方法,这正是我需要的。我转换为代码:whileb.digits.length当我比较这两种方法时,digits慢得多。时间./025/problem025.rb0.13s用户0.02s系统80%cpu0.190总计./025/problem025.rb2.19s用户0.0

  6. ruby - Ruby 性能中的计时器 - 2

    我正在寻找一个用ruby​​演示计时器的在线示例,并发现了下面的代码。它按预期工作,但这个简单的程序使用30Mo内存(如Windows任务管理器中所示)和太多CPU有意义吗?非常感谢deftime_blockstart_time=Time.nowThread.new{yield}Time.now-start_timeenddefrepeat_every(seconds)whiletruedotime_spent=time_block{yield}#Tohandle-vesleepinteravalsleep(seconds-time_spent)iftime_spent

  7. ruby-on-rails - 如果条件与 &&,是否有任何性能提升 - 2

    如果用户是所有者,我有一个条件来检查说删除和文章。delete_articleifuser.owner?另一种方式是user.owner?&&delete_article选择它有什么好处还是它只是一种写作风格 最佳答案 性能不太可能成为该声明的问题。第一个要好得多-它更容易阅读。您future的自己和其他将开始编写代码的人会为此感谢您。 关于ruby-on-rails-如果条件与&&,是否有任何性能提升,我们在StackOverflow上找到一个类似的问题:

  8. ruby - 如何理解 Ruby 中的发送者和接收者? - 2

    我很难理解Ruby中sender和receiver的实际含义。它们一般是什么意思?到目前为止,我只是将它们理解为方法调用和获取其返回值的调用。但是,我知道我的理解还远远不够。谁能给我一个Ruby中发送者和接收者的具体解释? 最佳答案 面向对象中的一个核心概念是消息传递和早期概念化,这在很大程度上借鉴了计算的Actor模型。艾伦·凯(AlanKay)创造了面向对象一词并发明了最早的OO语言之一SmallTalk,他拥有voicedregretatusingatermwhichputthefocusonobjectsinsteadofo

  9. ruby-on-rails - Rails - 理解 application.js 和 application.css - 2

    rails新手。只是想了解\assests目录中的这两个文件。例如,application.js文件有如下行://=requirejquery//=requirejquery_ujs//=require_tree.我理解require_tree。只是将所有JS文件添加到当前目录中。根据上下文,我可以看出requirejquery添加了jQuery库。但是它从哪里得到这些jQuery库呢?我没有在我的Assets文件夹中看到任何jquery.js文件——或者直接在我的整个应用程序中没有看到任何jquery.js文件?同样,我正在按照一些说明安装TwitterBootstrap(http:

  10. ruby - 如何找到我的 Ruby 应用程序中的性能瓶颈? - 2

    我编写了一个Ruby应用程序,它可以解析来自不同格式html、xml和csv文件的源中的大量数据。我如何找出代码的哪些区域花费的时间最长?有没有关于如何提高Ruby应用程序性能的好资源?或者您是否有任何始终遵循的性能编码标准?例如,你总是用加入你的字符串吗?output=String.newoutput或者你会使用output="#{part_one}#{part_two}\n" 最佳答案 好吧,有一些众所周知的做法,例如字符串连接比“#{value}”慢得多,但是为了找出您的脚本在哪里消耗了大部分时间或比所需时间更多,您需要进行分

随机推荐