原文地址:如何基于Spark Web UI进行Spark作业的性能调优
前言
在处理Spark应用程序调优问题时,我花了相当多的时间尝试理解Spark Web UI的可视化效果。Spark Web UI是分析Spark作业性能的非常方便的工具,但是对于初学者来说,仅从这些分散的可视化页面数据很难获取到有用的信息。因此,我感到有必要记录并分享我的学习经验。
目标和受众
本篇文章将帮助初学者从Spark Web UI中识别应用程序运行中可能存在的性能问题。重点仅在于UI中不明显的信息以及从此类信息中推断出的结论。请注意,本文并不包含从Spark Web UI中解释的详尽信息列表,而只包含我在项目中发现的相关信息,但对于读者来说足够通用。
Spark Web UI
Spark Web UI只有在应用程序运行时才可用。要分析过去的运行情况,需要启用历史服务器以存储事件日志,然后可以使用该日志来填充Web UI。
Spark Web UI在标签中显示有关应用程序的有用信息,即:
-
Executors
-
Environment
-
Jobs
-
Stages
-
Storage
下面将对这几个选项卡逐个进行讲解。
Executors
该选项卡提供有关每个执行者运行的任务的信息。
图1:执行者标签页摘要
在图1中,可以了解到有1个驱动程序和5个执行者,每个执行者都使用2个内核和3 GB内存运行。
红色框标记显示了任务的不均匀分布,其中一组节点正在进行大量任务,而其他节点相对空闲。
蓝色框标记显示输入数据大小为487.3 MB。现在,这个应用程序是在83 MB的数据集上运行的。输入数据大小包括读取的原始数据集和节点间的shuffle数据传输。这表明应用程序中进行了大量数据(约400 MB以上)的shuffle操作。
Environment
有很多Spark属性可以控制和优化应用程序。这些属性可以在提交作业或创建上下文对象时设置。除非属性被显式添加,否则它不会被应用。我们曾经误解了这一点,认为当没有显式设置时,属性会应用它们的默认值。所有已应用的属性可以从环境选项卡中查看。如果该属性在那里看不到,则表示该属性根本没有被应用。
Jobs
Jobs选项卡用于显示与Resilient Distributed Dataset依赖关系相关联的作业,它们以类似于Fig 2的有向无环图(DAG)的形式组织。从DAG可视化中,可以找到正在执行的阶段以及跳过的阶段数。默认情况下,Spark不会重用阶段中计算的步骤,除非明确地进行持久化/缓存。跳过的阶段是灰色标记的缓存阶段,其中计算值存储在内存中,访问HDFS后不会重新计算。通过查看DAG可视化,就足以了解RDD计算是否重复执行或使用缓存阶段。
图2:作业的DAG可视化
Stages
该选项卡提供了应用程序在任务级别上运行的深入视图。一个阶段代表由单个任务并行完成的工作段。任务与数据分区之间存在1对1的映射,即每个数据分区对应一个任务。用户可以从Spark Web UI深入到一个作业、特定阶段,以及每个阶段中的每个任务。阶段提供了执行的概览 - DAG可视化,事件时间轴,其任务的摘要/聚合指标。我更喜欢查看事件时间轴来分析任务。它们提供了阶段执行所花费时间的详细图形表示。通过一次快速扫描,我们可以快速推断出阶段的执行效果以及如何进一步提高执行时间。
图3-事件时间轴示例
例如,从图3中得出的结论可能是:
-
数据被分成了15个分区。因此有15个任务在运行(用15个绿色线表示)。
-
任务在3个节点上执行,每个节点有2个执行器。
-
只有当最长运行的任务完成时,该阶段才会完成。其他执行器在最长任务完成之前保持空闲。
-
有一些长时间运行的任务,而另一些任务运行时间非常短,表明数据没有被很好地分区。
-
这个阶段没有花费很多时间在调度延迟或序列化上,这是好的。
图4 - 具有许多数据分区的阶段的事件时间轴。
从图4可以看出,数据分布不均,分区过多。根据评估指标可以确认,任务调度所花费的时间比实际执行时间更长。时间轴中绿色部分所占比例越高,阶段计算效率越高。
希望作业中有更少的阶段。每当数据被shuffle时,就会创建一个新的阶段。shuffle成本高昂,因此尽量减少程序所需的阶段数。
Input Data Size
另一个重要的见解是查看shuffle数据的输入大小。其中一个目标也是减少这个shuffle数据的大小。
上面的图5显示了在移动数据时使用的MB级别的阶段。这表明可以改进代码以减少在阶段之间传递的数据量。例如,假设在给定事件“x”上应用了某些数据的过滤器,那么在生成的RDD中,“事件”列就变得多余了,因为从技术上讲,所有行都是事件“x”。在构建基于此过滤数据的未来RDD时,可以从中删除此列,以保存在shuffle操作期间传输的附加信息。
Storage
该图仅显示已持久化的RDD,即使用persist()或cache()方法进行持久化的RDD。为了使其更易读,可以在存储RDD时使用setName()方法为其分配名称。只有想要持久化的RDD应该显示在存储选项卡中,并且可以通过提供自定义名称轻松识别它们。
总结
本文帮助读者了解如何从Spark Web UI中识别问题,例如数据shuffle的大小、阶段执行时间、由于缺乏缓存而重新计算RDD等。如果了解自己的数据和应用程序,那么可以从执行UI中推断出理想的数据分布和所需的分区数。UI还可以显示集群中某个节点的负载过重,这也是需要改进的领域。本文还提到了一些解决这些问题的方法,更多内容可以参考Apache Spark官网关于性能调优的文档。
https://spark.apache.org/docs/latest/sql-performance-tuning.html