监控与调试
2026/9/19 3:53:29 网站建设 项目流程

监控级别

Spark应用程序和作业

无论是为了调试还是更好地理解应用程序在集群上的执行过程,通过Spark UI和Spark日志是最方便获取监控报告的方式,这些报告包括Spark应用程序的运行状态信息,例如RDD转换和查询计划的执行信息等。

JVM

Spark在Java虚拟机(JVM)上运行执行器,因此,下一个监视层次是监控虚拟机(VM)以更好地理解代码的运行方式。JVM提供一些监视工具,如用于跟踪堆栈的jstack,用于创建堆转储的jmap,用于报告时序统计信息的jstat,以及用于可视化JVM属性的jconsole,这些工具对于那些熟悉JVM内部机理的人员非常有用。你也可以使用像jvisualvm这样的工具来帮助分析Spark作业。其中一些JVM监视信息已经在Spark UI中提供了,但对于更低层次的调试,上述工具可派上用场。

操作系统/主机

JVM运行在主机操作系统(OS)上,监视这些机器的运行状态也很重要,这包括监控诸如CPU,网络,I/O等,这些信息通常在集群级监控方案中也会报告,但是你可以使用更专业的工具来获得更详细的监视信息,这些工具包括dstat,iostat和iotop

集群

当然,你也可以监视运行Spark应用程序的集群,这可能是Yarn,Mesos或Standalone集群,集群监控方案很重要,如果集群不正常工作,你需要很快知道,一些流行的集群级监控工具包括Ganglia和Prometheus

要监视什么

需要监控的主要有两个方面:运行应用程序的进程信息(CPU使用率,内存使用率等)以及查询执行过程(作业和任务)

驱动器和执行器进程

当监控一个Spark应用程序时,最需要注意的是驱动器进程,应用程序的所有状态都会在驱动器进程上有所反映,你需要确保它正确而稳定的运行。如果你只能监控一台机器或一台JVM,那首选就是驱动器节点。当然,了解执行器的状态对于监控Spark作业也非常重要,Spark提供一个基于Dropwizard Metrics Library的可配置指标监视系统,它的配置文件一般在$SPARK_HOME/conf/metrics.properties中指定,可以通过更改spark.metrics.conf配置属性来自定义配置文件位置,这些监控指标可以输出到包括Ganglia等多种不同的监控系统

查询、作业、阶段和任务

尽管监视驱动器和执行器进程很重要,但有时你还需要对特定查询级别的进程进行调试。两种最常见的监视方式:通过Spark日志和Spark UI

Spark日志

获取最详细Spark监视信息的方法之一就是通过日志文件。
Spark日志中记录的反常事件,或在Spark应用程序中有意添加的输出,都可以帮助你发现导致作业执行失败的原因。

Spark UI

Spark UI提供了一种可视化的方式在Spark和JVM级别来监视运行中的应用程序以及Spark工作负载的性能指标。每个运行的SparkContext都将启动一个WebUI,默认情况下在端口4040,它将列出应用程序的有用信息,例如,在本地模式下运行Spark时,通过访问http://localhost:4040即可在本地计算机上查看Web UI,如果你运行多个应用程序,他们将各自启动一个Web UI,并累加端口号(4041,4042,…),集群管理器还会从它自己的用户界面连接到每个应用程序的Web UI

如下展示了Spark UI中所有的可用选项卡

Jobs选项卡对应Spark作业
Stages对应各个阶段
Storage:包含当前在Spark应用程序中缓存的信息和数据
Environment:包含有关Spark应用程序的配置等相关信息
Executors:提供应用程序的每个执行器的详细信息
SQL:对应我们提交的结构化API查询(包括SQL和DataFrame)

调试和Spark抢救方案

缓慢任务或落后者

此问题在优化应用程序时非常常见,这可能是由于工作负载没有被均匀分布在集群各节点上(导致负载“倾斜”),或者是由于某台计算节点比其他计算节点速度慢(例如,由于硬件问题)

表现形式

以下都可能是该问题的表现形式:

  1. Spark阶段中只剩下少数任务未完成,这些任务运行了很长时间
  2. 在Spark UI中可以观察到这些缓慢的任务始终在相同的数据集上发生
  3. 各阶段都有这些缓慢任务
  4. 扩大Spark集群规模并没有太大的效果,有些任务仍然比其他任务耗时更长
  5. 在Spark指标中,某些执行器进程读取和写入的数据量比其他执行器进程大的多

应对措施

缓慢任务通常被称为“落后者”,有很多原因会导致缓慢任务,但最常见的原因是你的数据不均匀地分布到DataFrame或RDD分区上,发生这种情况时,一些执行器节点可能需要比其他执行器节点更多的工作量。一个特别常见的情况是,你使用按键分组操作,对应其中一个键的数据比其他键多得多。在这种情况下,当你查看Spark UI时,你会看到某些节点shuffle的数据比其他大得多。

  1. 尝试增加分区数以减少每个分区被分配到的数据量
  2. 尽可能分配给执行器进程更多的内存
  3. 检查用户定义函数(UDF)是否在其对象分配或业务逻辑中有资源浪费的情况
  4. 尝试通过另一种列组合来重新分区,例如,当你使用ID列进行分区时,如果ID是倾斜分布的,那么就容易产生落后者。或者当你使用存在许多空值的列进行分区时,许多对应空值列的行都被集中分配到一台节点上,也会造成落后者,在后一种情况下,首先筛选出空值可能会有所帮助
  5. 监视有缓慢任务的执行器节点,并确定该执行器节点在其他作业上也总是执行缓慢任务,这说明集群中可能存在一个不健康的执行器节点,例如,磁盘空间不足的节点
  6. 检查用户定义函数(UDF)是否在其对象分配或业务逻辑中有资源浪费的情况,如果可能,尝试将它们转换为DataFrame代码
  7. 确保你的UDF或用户定义的聚合函数(UDAF)在足够小的数据上可以运行。通常情况下,聚合操作要将大量数据存入内存以处理对某个key的聚合操作,从而导致该执行器比其他执行器要完成更多的工作
  8. 使用Dataset时可能会出现另一个常见问题,由于Dataset执行大量的对象实例化并将记录转换为用户定义函数中的Java对象,这可能会导致大量垃圾回收。如果你使用Dataset,请查看Spark UI中的垃圾回收指标,以确定它们是否是导致缓慢任务的原因

缓慢的聚合操作

如果你的聚合操作速度较慢,请先查看“缓慢任务”部分的解决方案,尝试过那些之后,你可能会继续看到同样的问题。

表现形式

  1. 在执行groupby操作时产生缓慢任务
  2. 聚合操作之后的作业也执行的非常缓慢

应对措施

这个问题不能总是能够得到解决。如果你的作业中需要对存在数据倾斜的某个key执行聚合操作,那么如果你想在它们上执行聚合操作就是很慢。

  1. 在聚合操作之前增加分区数量可能有助于减少每个任务中处理的不同key的数量
  2. 增加执行器进程的内存配额也可以帮助缓解此问题。如果一个key拥有大量数据,这将允许其执行器进程更少地与磁盘交互数据并更快完成任务,尽管它可能仍然比处理其他key的执行器进程要慢得多。
  3. 如果你发现聚合操作之后的任务也很慢,这意味着你的数据集在聚合操作之后可能仍然不均衡。尝试调用repartition并对数据进行随机重新分区
  4. 确保涉及的所有过滤操作和select操作在聚合操作之前完成,这样可以保证只对需要执行聚合操作的数据进行处理,避免处理无关数据。Spark的查询优化器将自动为结构化API执行此操作
  5. 一些聚合操作本身也比其他聚合操作慢,例如,collect_listcollect_set是非常慢的聚合函数,因为它们必须将所有匹配的对象返回给驱动器进程,所以在代码中应该尽量避免使用这些聚合操作
  6. 确保空值被正确地表示(建议使用Spark的null关键字),不要用“”或“Empty”之类的空值表示,Spark优化器通常会在作业执行初期来跳过对null空值的处理,但它无法为你自己定义的空值形式进行此优化

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询