如何调好Spark的表演,相信很多没有经验的人都无能为力。为此,本文总结了问题产生的原因和解决方法,希望大家可以通过这篇文章来解决这个问题。
0、背景
一些集群火花任务执行缓慢,经常出错。无论参数如何变化,都可以优化其性能,解决随机错误频繁的问题。
看任务的历史操作,平均时间3h左右,极不稳定,偶尔会给出一个错误:
1、优化思路
任务的运行时间与什么有关?
(1)数据源大小差异
在有限的计算下,作业的运行时间与数据量呈正相关。在这个例子中,数据量基本稳定,可以消除对数幅度波动带来的问题:
(2)代码本身的逻辑缺陷
例如,代码重复创建和初始化变量、环境、RDD资源等。任意持久化数据等。并广泛使用shuffle运算符,如reduceByKey、join和其他运算符。
在这段100行代码中,总共有三个洗牌操作,任务被spark driver分成四个阶段进行串行执行。代码位置如下:
我们需要做的是从算法和业务的角度,尽可能减少洗牌和登台,提高并行计算的性能。这是一个很大的话题,这次就不赘述了。
(3)参数设置不合理
这个技能比较常见。让我们看一下前面的核心参数设置:
num-executors=10||20,executor-cores=1||2,executor-memory=10||20,driver-memory=20,spark.default.parallelism=64
假设我们的火花队列资源如下:
内存=1T,内核=400
这里有一些关于如何设置参数的技巧。首先,我们必须了解星火资源的配置和使用原则:
在默认的非动态资源分配场景中,spark是一个预应用的资源,在任务开始之前资源被独占,直到整个作业的所有任务都完成。例如,如果你在跳板上启动一个火花壳,并且从不退出或执行任务,它将总是占用所有应用的资源。(如果设置了num-executors,动态资源分配将无效)
注意上面这句话。spark的资源分配方法与mapreduce/hive有很大不同。如果不理解这个问题,会造成参数设置的其他问题。
例如,多少适合执行器核心?没有任务的并行性,整个队列资源将被独占消耗,其他同学的任务无法执行。例如,当num-executors=20个executors-cores=1个executors-memory=10时,上述任务将独占20个内核和200G内存3小时。
那么,根据这种情况下的任务,结合我们现有的资源,如何设置这五个核心参数呢?
1) executor_cores*num_executors不能太小或太大!一般不超过总队列核心的25%,比如总队列核心400,最大不超过100,最小不低于40,除非日志量很小。
2) executor_cores不应为1!否则工作过程中的线程数太少,一般2~4个为宜。
executor _ memory通常为6~10g,最大不超过20G,否则会导致GC成本高或资源严重浪费。
4) spark_parallelism一般是executor_cores*num_executors的1~4倍,系统默认值为64。如果不设置,任务会分批串行执行,或者大量内核闲置,造成资源严重浪费。
5)驱动记忆有个同学之前设置了20G。实际上,驱动程序并不做任何计算和存储,只是发出任务与纱线浏览器和task进行交互。除非你是火花壳,一般1-2g就够了。
火花存储器管理器:
6)spark.shuffle.memoryFraction(默认为0.2),也称为ExecutionMemory。这个内存区域用于解决混洗、连接、排序和ag等问题。
gregations 过程中为了避免频繁IO需要的buffer。如果你的程序有大量这类操作可以适当调高。
7)spark.storage.memoryFraction(默认0.6),也叫 StorageMemory。这片内存区域是为了解决 block cache(就是你显示调用dd.cache, rdd.persist等方法), 还有就是broadcasts,以及task results的存储。可以通过参数,如果你大量调用了持久化操作或广播变量,那可以适当调高它。
8)OtherMemory,给系统预留的,因为程序本身运行也是需要内存的, (默认为0.2)。Other memory在1.6也做了调整,保证至少有300m可用。你也可以手动设置 spark.testing.reservedMemory . 然后把实际可用内存减去这个reservedMemory得到 usableMemory。 ExecutionMemory 和 StorageMemory 会共享usableMemory * 0.75的内存。0.75可以通过 新参数 spark.memory.fraction 设置。目前spark.memory.storageFraction 默认值是0.5,所以ExecutionMemory,StorageMemory默认情况是均分上面提到的可用内存的。
例如,如果需要加载大的字典文件,可以增大executor中 StorageMemory 的大小,这样就可以避免全局字典换入换出,减少GC,在这种情况下,我们相当于用内存资源来换取了执行效率。
最终优化后的参数如下:
效果如下:
(4)通过执行日志分析性能瓶颈
最后的任务还需要一个小时,那这一个小时究竟耗在哪了?按我的经验和理解,一般单天的数据如果不是太大,不涉及复杂迭代计算,不应该超过半小时才对。
由于集群的 Spark History Server 还没安装调试好,没法通过 spark web UI 查看历史任务的可视化执行细节,所以我写了个小脚本分析了下前后具体的计算耗时信息,可以一目了然的看到是哪个 stage 的问题,有针对性的优化。
可以看到优化后的瓶颈主要在最后写 redis 的阶段,要把 60G 的数据,25亿条结果写入 redis,这对 redis 来说是个挑战,这个就只能从写入数据量和 kv 数据库选型两个角度来优化了。
(5)其它优化角度
当然,优化和高性能是个很泛、很有挑战的话题,除了前面提到的代码、参数层面,还有怎样防止或减少数据倾斜等,这都需要针对具体的场景和日志来分析,此处也不展开。
2、spark 初学者的一些误区
对于初学者来说 spark 貌似无所不能而且高性能,甚至在某些博客、技术人眼里 spark 取代 mapreduce、hive、storm 分分钟的事情,是大数据批处理、机器学习、实时处理等领域的银弹。但事实确实如此吗?
从上面这个 case 可以看到,会用 spark、会调 API 和能用好 spark,用的恰到好处是两码事,这要求咱们不仅了解其原理,还要了解业务场景,将合适的技术方案、工具和合适的业务场景结合——这世上本就不存在什么银弹。。。
说道 spark 的性能,想要它快,就得充分利用好系统资源,尤其是内存和CPU:核心思想就是能用内存 cache 就别 spill 落磁盘,CPU 能并行就别串行,数据能 local 就别 shuffle。
看完上述内容,你们掌握怎么进行Spark的性能调优的方法了吗?如果还想学到更多技能或想了解更多相关内容,欢迎关注行业资讯频道,感谢各位的阅读!
内容来源网络,如有侵权,联系删除,本文地址:https://www.230890.com/zhan/149266.html