Spark WebUI详解

https://spark.apache.org/docs/latest/web-ui.html

Spark Web UI 是 Spark 集群监控、任务调优的核心工具,通过它可实时查看应用程序的运行状态、任务执行详情、资源分配等关键信息。

前置说明:本文基于 Spark 3.3.0 版本,Java 代码基于 Maven 依赖构建,核心依赖如下:

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-core_2.12</artifactId>
    <version>3.3.0</version>
</dependency>
<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-sql_2.12</artifactId>
    <version>3.3.0</version>
</dependency>

启动 Spark 应用并访问 Web UI

先通过一段简单的 Java 代码启动一个 Spark 应用(WordCount 案例),后续所有界面指标解读均围绕该任务展开

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import scala.Tuple2;

import java.util.Arrays;

public class SparkWebUISample {
    public static void main(String[] args) {
        // 1. 配置 Spark 应用(本地模式,便于调试,Web UI 自动启动)
        SparkConf conf = new SparkConf()
                .setAppName("SparkWebUISample") // 应用名称(Web UI 中显示)
                .setMaster("local[2]"); // 本地2核运行,模拟集群资源
        // 2. 创建 SparkContext(核心入口)
        JavaSparkContext sc = new JavaSparkContext(conf);
        // 3. 模拟任务:WordCount(读取文本、分词、统计)
        // 模拟输入数据(可替换为本地文件路径,如 "D:/test.txt")
        String input = "spark web ui spark web ui index spark core";
        JavaRDD<String> lines = sc.parallelize(Arrays.asList(input.split(" ")));
        // 分词、统计(核心计算逻辑,会产生 Task)
        JavaPairRDD<String, Integer> wordCounts = lines
                .mapToPair(word -> new Tuple2<>(word, 1))
                .reduceByKey(Integer::sum);
        // 输出结果(触发任务执行)
        wordCounts.foreach(println);
        // 阻塞程序,便于访问 Web UI(运行后访问  http://localhost:4040 )
        try {
            Thread.sleep(600000); // 阻塞10分钟
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 关闭 SparkContext
        sc.stop();
    }
}

编译并打成jar包:

在集群模式下,为了让日志持久化,需要做两件事:编辑 conf/spark-defaults.conf,让 Spark 知道往哪写、在哪读:

spark.eventLog.enabled           true
spark.eventLog.dir               file:///tmp/spark-events
spark.history.fs.logDirectory    file:///tmp/spark-events

提交应用

image

点进一个应用

image

运行该程序后,打开浏览器访问 http://localhost:4040,即可进入 Spark Web UI 主界面

另外一个示例程序:

package demo;

import org.apache.spark.SparkConf;
import org.apache.spark.api.java.JavaPairRDD;
import org.apache.spark.api.java.JavaRDD;
import org.apache.spark.api.java.JavaSparkContext;
import org.apache.spark.api.java.function.FlatMapFunction;
import org.apache.spark.api.java.function.Function2;
import org.apache.spark.api.java.function.PairFunction;
import scala.Tuple2;

import java.util.Arrays;
import java.util.Iterator;

public class JavaWordCount {
    public static void main(String[] args) {
        // 创建spark上下文
        SparkConf sparkConf = new SparkConf().setAppName("WordCount").setMaster("local");
        JavaSparkContext javaSparkContext = new JavaSparkContext(sparkConf);
        // 读取数据
        JavaRDD<String> lines = javaSparkContext.textFile("data/input/word.txt");
        // 扁平化
        JavaRDD<String> wordRDD = lines.flatMap(new FlatMapFunction<String, String>() {
            @Override
            public Iterator<String> call(String line) throws Exception {
                return Arrays.asList(line.split(" ")).iterator();
            }
        });

        // 计数 聚合
        // PairFunction<传入参数,传出参数,传出参数>
        // PairFunction<word, word, 1>
        JavaPairRDD<String, Integer> wordAndOneRDD = wordRDD.mapToPair(new PairFunction<String, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(String word) throws Exception {
                return new Tuple2<>(word, 1);
            }
        });

        // 聚合
        // Tuple的值是integer
        // Function2<Tuple1.value, Tuple2.value, 结果>
        JavaPairRDD<String, Integer> wordCountRDD = wordAndOneRDD.reduceByKey(new Function2<Integer, Integer, Integer>() {
            @Override
            public Integer call(Integer v1, Integer v2) throws Exception {
                return v1 + v2;
            }
        });

        // 调换顺序, 因为只能根据key排序, 所以要反转键值对
        // <word,count>  ->  <count,word>
        JavaPairRDD<Integer, String> countWordRDD = wordCountRDD.mapToPair(new PairFunction<Tuple2<String, Integer>, Integer, String>() {
            @Override
            public Tuple2<Integer, String> call(Tuple2<String, Integer> tp) throws Exception {
                return tp.swap();
            }
        });

        // 降序排序
        JavaPairRDD<Integer, String> countWordSortRDD = countWordRDD.sortByKey(false);
        // <count,word> ->  <word,count>
        JavaPairRDD<String, Integer> resultRDD = countWordSortRDD.mapToPair(new PairFunction<Tuple2<Integer, String>, String, Integer>() {
            @Override
            public Tuple2<String, Integer> call(Tuple2<Integer, String> tp) throws Exception {
                return tp.swap();
            }
        });

        // 打印
        resultRDD.collect().forEach(o -> System.out.println(o));
        // 关闭连接
        javaSparkContext.stop();
    }
}

核心界面及指标详解

按 Web UI 导航栏顺序分为 6 个部分:Jobs、Stages、Tasks、Storage、Environment、Executors,部分版本会包含 History Server 相关界面。

Jobs 界面(任务整体运行状态)

Jobs选项卡显示Spark应用程序中所有作业的摘要页面以及每个作业的详细信息页面。摘要页面显示高级信息,如所有作业的状态、持续时间和进度以及整体事件时间线。当您在摘要页面上单击某个作业时,会看到该作业的详细信息页面。详细信息页面进一步显示事件时间线、DAG可视化以及作业的所有Stage。

Jobs 界面是 Web UI 的入口,展示当前应用中所有 Job 的运行状态,每个 Job 对应代码中一次「行动操作」(如 foreach、count、save 等,本文中 wordCounts.foreach(println) 即为一个 Job)。

核心指标详解

  • Job ID:Job 的唯一标识,按执行顺序递增(本文任务中只有1个 Job,ID 为0)。Spark 中一个行动操作对应一个 Job,多个行动操作会产生多个 Job。

  • Description:Job 的描述,默认是触发 Job 的行动操作代码(如本文中显示「foreach at SparkWebUISample.java:35」,对应代码中 wordCounts.foreach(println) 所在行),可通过sc\.setJobDescription\(\&\#34;自定义描述\&\#34;\) 修改。

  • Submitted:Job 提交时间,格式为「yyyy-MM-dd HH:mm:ss」,对应代码中行动操作开始执行的时间。

  • Duration:Job 总执行时间,从提交到完成的耗时(单位:ms/s),核心优化指标——耗时过长可能是任务并行度不足、数据倾斜等问题。本文任务数据量小,Duration 通常在 100ms 以内。

  • Stages:该 Job 包含的 Stage 数量(本文任务中为2个 Stage,后续 Stages 界面详解),格式为「成功数/总数」(如 2/2 表示2个 Stage 均成功)。

  • Tasks:该 Job 包含的 Task 总数(本文任务中为4个 Task),格式为「成功数/总数/失败数」,Task 是 Spark 执行的最小单位。

  • Input:Job 读取的输入数据量(本文任务为内存中模拟数据,输入量较小,显示为「0 B」;若读取本地/分布式文件,会显示具体大小,如 100 MB)。

  • Output:Job 输出的数据量(本文任务为打印到控制台,输出量为「0 B」;若保存到文件,会显示具体大小)。

  • Shuffle Read:Shuffle 过程中读取的数据量(本文任务中 reduceByKey 会触发 Shuffle,会显示具体大小,如 100 B),Shuffle 是性能瓶颈之一,该值过大需优化。

  • Shuffle Write:Shuffle 过程中写入的数据量(与 Shuffle Read 对应,本文任务中会有少量数据写入)。

  • Status:Job 运行状态,包括「Running(运行中)、Succeeded(成功)、Failed(失败)、Killed(被杀死)」,本文任务运行后显示为 Succeeded。

点击 Job ID(如 0),可进入该 Job 的详情页,查看其包含的所有 Stage、Task 详情,是排查 Job 执行缓慢的入口。

Stages 界面(任务分阶段执行详情)

Spark 会将一个 Job 拆分为多个 Stage(阶段),拆分依据是「是否存在 Shuffle 操作」——有 Shuffle 操作则拆分 Stage,无 Shuffle 操作则合并为一个 Stage。本文 Java 任务中,reduceByKey 是 Shuffle 操作,因此 Job 0 被拆分为 2 个 Stage:
Stage 0:mapToPair(分词、生成键值对),无 Shuffle,属于「Map Stage」;
Stage 1:reduceByKey + foreach(统计、输出),有 Shuffle,属于「Reduce Stage」。

包含 Stage ID、Description、Status、Tasks、Duration 等列)

核心指标详解

顶部汇总指标

  • Active Stages:正在运行的 Stage 数量(本文任务运行时为1,运行完成后为0);

  • Completed Stages:已完成的 Stage 数量(本文任务完成后为2);

  • Failed Stages:失败的 Stage 数量(正常运行时为0,失败时显示具体数量);

  • Total Stages:总 Stage 数量(本文任务为2)。

表格指标

  • Stage ID:Stage 的唯一标识,按执行顺序递增(本文中为0、1),Stage 0 执行完成后才会执行 Stage 1(因 Stage 1 依赖 Stage 0 的输出)。

  • Description:Stage 的描述,对应代码中的计算逻辑(如 Stage 0 显示「mapToPair at SparkWebUISample.java:31」,对应 mapToPair 操作;Stage 1 显示「reduceByKey at SparkWebUISample.java:32 + foreach at ...」)。

  • Status:Stage 运行状态,与 Job 状态一致(Running/Succeeded/Failed/Killed)。

  • Tasks:该 Stage 包含的 Task 数量(本文中 Stage 0 为2个 Task,Stage 1 为2个 Task,对应代码中 setMaster(&#34;local[2]&#34;) 的2核配置,并行度为2)。

  • Duration:Stage 执行总时间,若某 Stage 耗时过长,需重点排查(如数据倾斜导致某 Task 拖慢整体)。

  • Input Size / Records:该 Stage 读取的数据量(Size)和记录数(Records),Stage 0 读取模拟数据,Records 为8(input 字符串拆分后有8个单词),Size 为几十字节。

  • Output Size / Records:该 Stage 输出的数据量和记录数,Stage 0 输出8个键值对(如 (spark,1)、(web,1)),Records 为8;Stage 1 输出4个统计结果(如 (spark,3)),Records 为4。

  • Shuffle Read / Write:仅 Shuffle Stage(本文中 Stage 1)有值,Map Stage(Stage 0)无值。Shuffle Read 是 Stage 1 从 Stage 0 读取的 Shuffle 数据量,Shuffle Write 是 Stage 0 写入的 Shuffle 数据量。

  • Errors:该 Stage 执行过程中出现的错误数(正常运行时为0,失败时显示错误详情,点击可查看堆栈信息)。

点击 Stage ID(如 0),可进入该 Stage 的详情页,查看每个 Task 的执行详情、耗时分布、数据处理情况,是排查 Stage 执行缓慢的核心入口。

Tasks 界面(最小执行单元详情)

Task 是 Spark 执行的最小单元,每个 Stage 包含多个 Task(数量由并行度、数据分区数决定),本文任务中每个 Stage 有2个 Task,共4个 Task。Tasks 界面可按 Stage 筛选,查看所有 Task 的详细信息。

(注:包含 Task ID、Stage ID、Status、Duration、Input/Output 等列)

核心指标详解

  • Task ID:Task 的唯一标识,在整个应用中递增(本文中为0、1、2、3)。

  • Stage ID:该 Task 所属的 Stage ID(如 Task 0、1 属于 Stage 0;Task 2、3 属于 Stage 1)。

  • Executor ID:执行该 Task 的 Executor 标识(本地模式下为 driver,集群模式下为 worker 节点的 Executor ID)。

  • Status:Task 运行状态,包括「Pending(等待)、Running(运行中)、Succeeded(成功)、Failed(失败)、Killed(被杀死)」。

  • Duration:单个 Task 的执行时间,核心优化指标——若多个 Task 耗时差异过大(如有的 10ms,有的 1000ms),大概率存在数据倾斜(某 Task 处理的数据量远超其他 Task)。本文任务数据均匀,所有 Task 耗时相近。

  • Input Size / Records:该 Task 读取的数据量和记录数(Stage 0 的每个 Task 读取4个单词,Records 为4;Stage 1 的每个 Task 读取4个键值对,Records 为4)。

  • Output Size / Records:该 Task 输出的数据量和记录数(Stage 0 的每个 Task 输出4个键值对;Stage 1 的每个 Task 输出2个统计结果)。

  • Shuffle Read / Write:Stage 0 的 Task 有 Shuffle Write(写入 Shuffle 数据),无 Shuffle Read;Stage 1 的 Task 有 Shuffle Read(读取 Stage 0 写入的数据),无 Shuffle Write。

  • GC Time:该 Task 执行过程中 JVM 垃圾回收的时间(单位:ms),GC Time 过长会导致 Task 耗时增加,需优化 JVM 参数(如调整堆内存大小)。

  • Result Serialization Time:Task 结果序列化的时间(单位:ms),序列化耗时过长可优化序列化方式(如使用 Kryo 序列化替代默认的 Java 序列化)。

  • Getting Result Time:获取 Task 结果的时间(单位:ms),通常耗时较短,若过长可能是网络延迟。

点击 Task ID,可进入该 Task 的详情页,查看其执行日志、数据处理详情、GC 日志等,是排查单个 Task 失败、耗时过长的关键入口。

Storage 界面(RDD 存储详情)

Storage 界面展示当前应用中所有持久化(cache/persist)的 RDD 信息,若代码中未对 RDD 进行持久化(如 rdd\.cache\(\)),该界面为空。修改上述 Java 代码,添加 RDD 持久化,便于解读指标:

// 在 mapToPair 后添加持久化操作
JavaPairRDD<String, Integer> pairRDD = lines
        .mapToPair(word -> new Tuple2<>(word, 1))
        .cache(); // 持久化 RDD 到内存

// 后续 reduceByKey 基于持久化的 RDD 计算
JavaPairRDD<String, Integer> wordCounts = pairRDD.reduceByKey(Integer::sum);

修改后,运行程序,Storage 界面会显示 pairRDD 的存储详情。

(注:包含 RDD Name、Storage Level、Size、Partitions 等列)

核心指标详解

  • RDD Name:持久化的 RDD 名称,默认是代码中 RDD 对应的操作(如「MapPartitionsRDD[2]」,可通过 rdd\.setName\(\&\#34;自定义名称\&\#34;\) 修改,便于识别)。

  • Storage Level:RDD 的持久化级别,本文中 cache() 对应「MEMORY_ONLY」(仅存储在内存中),其他常见级别:

    • MEMORY_AND_DISK:内存不足时存储到磁盘;

    • DISK_ONLY:仅存储到磁盘;

    • MEMORY_ONLY_SER:内存中序列化存储(节省内存)。

  • Size in Memory:RDD 在内存中的大小(本文中 pairRDD 较小,约几十字节),若 RDD 过大,会导致内存不足,触发磁盘存储或 GC。

  • Size on Disk:RDD 在磁盘中的大小(本文中为0,因持久化级别为 MEMORY_ONLY,内存充足时不写入磁盘)。

  • Partitions:RDD 的分区数(本文中为2,对应 local[2] 的并行度,可通过rdd\.repartition\(4\) 修改分区数)。

  • Replication:RDD 分区的副本数(默认是1,集群模式下可通过 rdd\.persist\(StorageLevel\.MEMORY\_ONLY\_2\) 设置为2,提高容错性)。

  • Use Memory:该 RDD 占用的内存比例(本文中接近100%,因数据量小)。

  • Use Disk:该 RDD 占用的磁盘比例(本文中为0)。

  • Actions:基于该 RDD 执行的行动操作次数(本文中为1,即 reduceByKey 后的 foreach 操作)。

点击 RDD Name,可查看该 RDD 的每个分区的存储详情(如分区 ID、存储位置、大小等),若 RDD 持久化后占用内存过大,可通过调整持久化级别、减少分区数、过滤无用数据等方式优化。

Environment 界面(核心:环境配置详情)

Environment 界面展示当前 Spark 应用的所有环境配置信息,包括 Spark 版本、JVM 配置、系统环境变量、依赖包、核心配置参数等,用于排查配置错误(如内存配置不足、并行度设置不合理等)。

(注:包含 Spark Properties、System Properties、Classpath Entries 等模块)

核心模块及指标详解

Spark Properties

  • spark.app.name:Spark 应用名称(本文中为「SparkWebUISample」,对应代码中 setAppName 配置);

  • spark.master:Spark 运行模式(本文中为「local[2]」,本地2核;集群模式下为「yarn」「spark://host:port」等);

  • spark.driver.memory:Driver 进程的内存大小(默认1g,可通过 \-\-driver\-memory 2g 调整);

  • spark.executor.memory:Executor 进程的内存大小(本地模式下默认与 Driver 内存一致,集群模式下可通过 \-\-executor\-memory 2g 调整);

  • spark.executor.cores:每个 Executor 的核心数(本地模式下为2,对应 local[2];集群模式下可通过 \-\-executor\-cores 4 调整);

  • spark.default.parallelism:默认并行度(本地模式下为核心数,即2;集群模式下默认是所有 Executor 核心数之和);

  • spark.serializer:序列化方式(默认是「org.apache.spark.serializer.JavaSerializer」,可改为「org.apache.spark.serializer.KryoSerializer」优化性能)。

System Properties

展示 JVM 系统属性,如 JDK 版本、JVM 堆内存配置(-Xms、-Xmx)、系统编码等,用于排查 JVM 相关问题(如堆内存不足导致的 OOM)。

Classpath Entries

展示 Spark 应用的类路径,包括依赖包(如 spark-core、spark-sql 依赖)、本地文件路径等,用于排查依赖缺失问题(如 ClassNotFoundException)。

Hadoop Properties(可选)

若 Spark 应用读取 Hadoop 相关数据(如 HDFS 文件),会显示 Hadoop 的配置信息(如 fs.defaultFS、hadoop.home.dir 等)。

Executors 界面(资源分配与使用详情)

Executors 界面展示当前应用中所有 Executor 的资源分配、使用情况,包括 Driver 和 Worker 节点的 Executor(本地模式下只有 Driver 对应的 Executor),集群模式下会显示所有 Worker 节点的 Executor 信息。

Executor、Task、Job、Stage之间的关系

Spark中Job、Stage、Task是计算逻辑的层次关系,Executor是执行Task的物理资源。一个Action触发一个Job;Job根据Shuffle依赖划分成多个Stage;Stage内包含并行的Task集合;Task在Executor中运行。

  • Job (作业):由 Action 算子触发,包含一个或多个Stage。
  • Stage (阶段):Stage由宽依赖划分(Shuffle),Stage间串行,Stage内窄依赖任务并行。
  • Task (任务):最小计算单元,一个Task对应一个RDD分区,运行于Executor。
  • Executor (执行器):运行在Worker节点上的JVM进程,负责执行Task

Job 与 Stage:横向切分

调用一个 Action(如 count(), save(), collect())时,Spark 就会启动一个 Job。 Spark 会根据宽依赖(Wide Dependency,即是否需要 Shuffle)将 Job 划分为多个 Stage。

窄依赖:如 map, filter,数据不需要跨节点打乱,可以在一个 Stage 内完成。
宽依赖:如 reduceByKey, join,需要将数据重新洗牌分配,必须划分为不同的 Stage。

Stage 与 Task:纵向并行

一个 Stage 包含一堆完全相同的业务逻辑,只是处理的数据分片(Partition)不同。1 个 Partition = 1 个 Task。如果一个 Stage 需要处理 100 个分区,它就会产生 100 个 Task。这些 Task 组成一个 TaskSet。

Task 与 Executor:物理执行

Executor 是 JVM 进程,它内部有多个 CPU Slots(线程池)。

Task 被发送到 Executor 后,占用一个 Slot 运行。一个 Executor 同一时间能跑多少个 Task,取决于它被分配了多少个 CPU 核心。

如果 Task 数量太少,说明并行度不够,Executor 的 CPU 会空转;如果 Task 太多,Task 调度开销会过大。通常建议 Task 数量设置为 CPU 总核心数的 2 到 3 倍。这里的“CPU 总核心数”指的是所有参与该任务的 Executor 所拥有的核心(Cores)总和。

如果有 10 个 Executor,每个 Executor 分配了 4 个 Cores,那么:Total_Cores = 10 * 4 = 40,按照 2 到 3 倍的建议, Task 数量(即分区数 Partition) 应该设置在 80 到 120 之间。

1 Core 对应 1 Task 是最完美的,实际上“多给一点”是为了填补数据倾斜或执行差异带来的时间差:

  1. 避免资源闲置(流水线效应):如果 Task 数等于总 Core 数,那么只要其中一个 Task 因为数据量稍大跑得慢了点,那个对应的 Core 就会一直被占用,而其他 Core 跑完后就会闲置,导致集群整体利用率下降。
  2. 长尾任务处理:如果 Task 更多(比如 3 倍),跑完快任务的 Core 可以立即去接手下一个待处理的 Task,从而让所有 Core 尽量同时完成工作。

虽然2-3 倍是黄金准则,但并非所有情况都只看 Core 数,有两个坑需要避开:

  1. 每个 Task 的数据量:
    如果 Task 太多,每个 Task 处理的数据量太小(比如只有几 KB),那么 Spark 调度 Task 的开销(序列化、网络传输等)反而会大于执行时间。建议:通常每个 Task 处理的数据量在 128MB 到 256MB 比较合适。

  2. 内存限制:
    如果一个 Core 跑一个 Task 时内存就很吃紧了,强行增加并行度(Task 数)会导致每个 Task 能分到的内存变少,从而引发 OOM (Out of Memory) 或频繁的 GC。

(注:包含 Executor ID、Host、Cores、Memory Used 等列)

核心指标详解

顶部汇总指标

  • Total Cores:所有 Executor 的总核心数(本文中为2,对应 local[2]);

  • Used Cores:已使用的核心数(任务运行时为2,任务完成后为0);

  • Total Memory:所有 Executor 的总内存(本文中为1g,默认 Driver 内存);

  • Used Memory:已使用的内存(任务运行时会占用部分内存,任务完成后会释放)。

表格指标

  • Executor ID:Executor 的唯一标识,本地模式下为「driver」(Driver 进程同时作为 Executor),集群模式下为数字(如 0、1、2)。

  • Host:Executor 所在的主机地址(本地模式下为「localhost」,集群模式下为 Worker 节点的 IP 地址)。

  • Cores:该 Executor 分配的核心数(本文中为2,对应 local[2]),核心数越多,并行执行的 Task 越多。

  • Memory Used:该 Executor 已使用的内存(包括 Task 执行内存、RDD 持久化内存等),若内存使用接近上限,会触发 GC 或 OOM。

  • Memory Total:该 Executor 分配的总内存(本文中为1g,可通过配置调整)。

  • Disk Used:该 Executor 已使用的磁盘空间(本文中为0,若 RDD 持久化到磁盘,会显示具体大小)。

  • Active Tasks:该 Executor 正在执行的 Task 数量(任务运行时为2,任务完成后为0)。

  • Failed Tasks:该 Executor 执行失败的 Task 数量(正常运行时为0)。

  • Completed Tasks:该 Executor 已完成的 Task 数量(本文中为4,对应所有 Task)。

  • Shuffle Read / Write:该 Executor 上所有 Task 的 Shuffle 读取/写入总数据量。

  • GC Time:该 Executor 上所有 Task 的 GC 总时间(单位:ms),GC 总时间过长需优化 JVM 内存配置。

点击 Executor ID(如 driver),可查看该 Executor 的详细信息,包括 Task 执行日志、GC 日志、内存使用详情等,是排查 Executor 内存不足、核心分配不合理的关键入口。

SQL/DataFrame 界面(SQL与DataFrame操作监控)

SQL/DataFrame 界面是 Spark SQL 相关任务的核心监控界面,当应用中执行 DataFrame/Dataset 操作、SQL 查询时,该界面会显示所有 SQL 语句的执行详情、执行计划、任务关联等信息,是排查 SQL 性能瓶颈、优化查询语句的关键入口。先补充 Java 代码(新增 DataFrame 操作),便于解读指标:

import org.apache.spark.sql.Dataset;
import org.apache.spark.sql.Row;
import org.apache.spark.sql.SparkSession;

public class SparkWebUISqlSample {
    public static void main(String[] args) {
        // 1. 配置并创建 SparkSession(SQL/Dataset 入口)
        SparkSession spark = SparkSession.builder()
                .appName("SparkWebUISqlSample")
                .master("local[2]")
                .getOrCreate();
        // 2. 模拟数据,创建 DataFrame(对应 SQL 表)
        String[] data = {"spark,3", "web,2", "ui,2", "index,1", "core,1"};
        Dataset<String> dataSet = spark.createDataset(Arrays.asList(data), Encoders.STRING());
        Dataset<Row> df = dataSet
                .withColumn("split", split(col("value"), ","))
                .withColumn("word", col("split").getItem(0))
                .withColumn("count", col("split").getItem(1).cast(DataTypes.IntegerType))
                .drop("split", "value");
        // 3. 执行 DataFrame 操作(对应 SQL 查询)
        // 操作1:筛选 count > 1 的数据
        Dataset<Row> filterDf = df.filter(col("count").gt(1));
        // 操作2:按 count 降序排序
        Dataset<Row> sortedDf = filterDf.orderBy(col("count").desc());
        // 执行行动操作,触发任务
        sortedDf.show();
        // 4. 执行 SQL 查询(需先注册临时表)
        df.createOrReplaceTempView("word_count");
        Dataset<Row> sqlResult = spark.sql("SELECT word, count FROM word_count WHERE count > 1 ORDER BY count DESC");
        sqlResult.show();
        // 阻塞程序,便于访问 Web UI
        try {
            Thread.sleep(600000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        // 关闭 SparkSession
        spark.stop();
    }
}

运行该程序后,Web UI 会新增 SQL/DataFrame 界面(导航栏直接显示),所有 DataFrame 操作、SQL 查询都会在该界面展示

注:包含 Query ID、Description、Duration、Jobs、Physical Plan 等列

核心指标详解

顶部汇总指标
  • Active Queries:正在执行的 SQL/DataFrame 查询数量(本文任务中最多2个,即上述两个查询,执行完成后为0);

  • Completed Queries:已完成的查询数量(本文任务完成后为2);

  • Failed Queries:执行失败的查询数量(正常运行时为0,失败时显示具体数量,点击可查看错误详情);

  • Total Queries:总查询数量(本文任务为2)。

表格核心指标
  • Query ID:SQL/DataFrame 查询的唯一标识,按执行顺序递增(本文中为0、1,分别对应 DataFrame 排序操作、SQL 查询)。

  • Description:查询描述,对应代码中的具体操作:

    • DataFrame 操作:显示操作链(如「orderBy(desc(count)) + filter(gt(count, 1))」);

    • SQL 查询:显示完整 SQL 语句(如「SELECT word, count FROM word_count WHERE count &gt; 1 ORDER BY count DESC」)。

  • Submitted:查询提交时间,格式与 Jobs 界面一致,对应代码中行动操作(show())执行的时间。

  • Duration:查询总执行时间(单位:ms/s),核心优化指标——耗时过长可能是 SQL 语句未优化、数据倾斜、Join 操作不合理等问题。

  • Jobs:该查询触发的 Job 数量(本文中每个查询触发1个 Job,格式为「成功数/总数」,如 1/1),点击 Job ID 可跳转到 Jobs 详情页。

  • Stages:该查询涉及的 Stage 总数(本文中每个查询对应2个 Stage,与之前 WordCount 任务逻辑一致)。

  • Tasks:该查询涉及的 Task 总数(本文中每个查询对应4个 Task,对应 local[2] 并行度)。

  • Input:查询读取的数据量和记录数(本文中读取5条模拟数据,Records 为5,Size 为几十字节)。

  • Output:查询输出的数据量和记录数(本文中筛选后输出3条数据,Records 为3)。

  • Status:查询运行状态,与 Job 状态一致(Running/Succeeded/Failed/Killed)。

关键子界面(点击 Query ID 进入)

点击某条查询的 Query ID,可进入该查询的详情页,核心包含3个关键子界面,是 SQL 优化的核心:

  • Details(详情):重复表格中的核心指标,补充查询的执行用户、提交节点等信息,无新增关键指标。

  • Physical Plan(物理执行计划):最核心的优化参考,展示 Spark 对 SQL/DataFrame 操作的解析和执行流程(如过滤、排序、扫描等步骤),标注每个步骤的耗时、数据量。例如本文中查询的物理计划会显示「Scan → Filter → Sort → CollectLimit」,若某步骤耗时过长,需针对性优化(如 Filter 步骤耗时久,可优化过滤条件;Sort 步骤耗时久,可调整分区数)。

  • Jobs/Stages/Tasks:关联该查询的所有 Job、Stage、Task 详情,点击可直接跳转到对应界面,便于联动排查问题(如查询耗时久,可直接查看关联 Task 的耗时分布)。

History Server 界面

上述界面均为「实时运行的 Spark 应用」的 Web UI,应用停止后,4040 端口会关闭,无法再查看。若需查看历史应用的运行详情,需启动 Spark History Server,配置如下:

  1. 修改 spark-defaults.conf,添加:
    spark\.eventLog\.enabled true spark\.eventLog\.dir hdfs://localhost:9000/spark\-history(或本地路径)

  2. 启动 History Server:spark\-submit \-\-class org\.apache\.spark\.deploy\.history\.HistoryServer

  3. 访问 History Server 界面:http://localhost:18080,可查看所有历史应用的 Web UI 详情(与实时界面一致)。

总结与调优建议

Spark Web UI 的核心价值是「监控任务运行状态、定位性能瓶颈」:

  1. 若 Job/Stage 耗时过长:查看 Tasks 界面的 Task 耗时分布,若存在数据倾斜(部分 Task 耗时远超其他),可通过 repartition 重新分区、过滤异常数据、使用 reduceByKey 替代 groupByKey(减少 Shuffle 数据量)优化;

  2. 若内存不足(OOM):查看 Executors 界面的 Memory Used 指标,调整 spark\.driver\.memoryspark\.executor\.memory 配置,或优化 RDD 持久化级别(如改用 MEMORY_AND_DISK);

  3. 若 Shuffle 数据量过大:查看 Jobs/Stages 界面的 Shuffle Read/Write 指标,优化计算逻辑,减少 Shuffle 操作(如尽量使用窄依赖操作,避免宽依赖)。

  4. 若 SQL/DataFrame 查询耗时过长:查看 SQL/DataFrame 界面的 Physical Plan,优化查询语句(如添加过滤条件、优化 Join 操作、调整排序分区数)。

posted @ 2025-10-22 21:56  vonlinee  阅读(102)  评论(0)    收藏  举报