每天认识一个组件:Apache Spark

一、引言

Apache Spark 诞生于 MapReduce 难以高效支撑多轮迭代、交互式分析和状态型流处理的背景下,它是一个面向大规模数据处理的统一分析引擎,它通过内存计算、DAG 执行、弹性分布式数据集和统一上层库,把过去割裂的批、流、SQL、机器学习和图计算能力整合到一个引擎中。

lua 复制代码
传统 MapReduce 多轮计算:

Input
  |
  v
MapReduce Job 1  ---> 写 HDFS
  |
  v
MapReduce Job 2  ---> 写 HDFS
  |
  v
MapReduce Job 3  ---> 写 HDFS
  |
  v
Output

问题:每一轮都频繁读写稳定存储,适合简单批处理,但不适合低延迟多轮计算。

Spark 多轮计算:

Input
  |
  v
RDD / DataFrame
  |
  +--> Transformation 1
  |
  +--> Transformation 2
  |
  +--> Transformation 3
  |
  v
Action / Output

特点:中间数据可缓存,执行计划形成 DAG,失败分区可通过 lineage 重算。

Spark 并没有否定 Hadoop 生态。Spark 可以读取 HDFS、对象存储和其他 Hadoop 支持的数据源,也可以运行在 YARN 之上;它更像是替代 MapReduce 计算引擎的一种通用执行层,而不是替代整个 Hadoop 生态。

二、Spark 架构原理

一个 Spark 应用本质上是一组运行在集群上的独立进程,由主程序中的SparkContext 协调;主程序所在进程称为 Driver Program。Driver 会连接 Cluster Manager(Spark Standalone、Hadoop YARN 和 Kubernetes),由后者在集群中为应用分配资源并启动 Executor,Executor 负责执行任务并保存应用运行过程中的数据。

lua 复制代码
Spark 集群运行架构:

                 +----------------------+
                 |    Driver Program    |
                 |  SparkContext / DAG  |
                 +----------+-----------+
                            |
                            | 申请资源 / 提交任务
                            v
                 +----------------------+
                 |   Cluster Manager    |
                 | Standalone/YARN/K8s  |
                 +----------+-----------+
                            |
          +-----------------+-----------------+
          |                                   |
          v                                   v
+-------------------+               +-------------------+
|   Worker Node A   |               |   Worker Node B   |
| +---------------+ |               | +---------------+ |
| |   Executor    | |               | |   Executor    | |
| | Task / Cache  | |               | | Task / Cache  | |
| +---------------+ |               | +---------------+ |
+-------------------+               +-------------------+

从执行逻辑看,Spark 会把一个用户动作拆成 Job、Stage 和 Task。Job 是由 Action 触发的一次并行计算,Stage 是 Job 根据依赖关系拆分出的任务集合,Task 是发送到某个 Executor 上运行的最小工作单元。

arduino 复制代码
一次 Spark 作业的逻辑拆分:

Action
  |
  v
Job
  |
  +-------------------+
  |                   |
  v                   v
Stage 1            Stage 2
  |                   |
  +----+----+         +----+----+
  |    |    |         |    |    |
Task Task Task      Task Task Task
  |    |    |         |    |    |
Executor 并行执行    Executor 并行执行

Spark 的执行模型里有一个很重要的概念:Transformation 和 Action。Transformation 描述数据如何变换,比如map 、filter 、join ;Action 触发真正执行,比如count 、collect 、save 。这种延迟执行机制让 Spark 有机会在真正运行前构建 DAG,并尽可能优化任务划分和数据流。

三、核心抽象

RDD 是 Spark 最早的核心抽象,定义为一个可容错、可并行操作的元素集合;它可以从外部存储系统读取,也可以由 Driver 中已有集合并行化创建。

RDD 的三个关键词是"分区、血缘、容错"。分区让数据能够分布到不同节点上并行计算;血缘记录一个 RDD 如何从父数据集变换而来;容错则依赖血缘在节点失败时重算丢失分区。这个设计让 Spark 在很多分析场景中不必把所有中间结果都复制多份。

scss 复制代码
RDD 血缘关系示意:

textFile("logs")
      |
      v
  filter(error)
      |
      v
  map(toPair)
      |
      v
  reduceByKey
      |
      v
  saveAsTextFile

如果某个分区丢失:
Spark 可根据 textFile -> filter -> map -> reduceByKey 的链路重算该分区。

随着 Spark SQL 的成熟,DataFrame 和 Dataset 成为更常见的开发接口。相比直接操作 RDD,DataFrame 更接近关系型表,包含 Schema 信息,适合 SQL、ETL、数据湖处理和 BI 查询,Spark 官方也将 Spark SQL、DataFrame、Dataset 描述为结构化数据处理的重要入口。

对于新项目,一般建议优先使用 DataFrame / SQL API。RDD 仍适合表达底层、非结构化、需要精细控制的计算逻辑,但大多数数仓 ETL、湖仓加工和分析型作业会从 Catalyst 优化器、列式执行、谓词下推等结构化处理能力中受益。

四、功能特性

Spark 的定位是"统一分析引擎",上层工具包括 Spark SQL、pandas API on Spark、MLlib、GraphX 和 Structured Streaming, Spark 的能力范围已经超过传统批处理框架。

能力 说明 常见场景
Spark Core 提供任务调度、内存管理、RDD、容错等底层能力 自定义分布式计算
Spark SQL 支持 SQL、DataFrame、Dataset 和结构化数据处理 数仓 ETL、湖仓查询、报表加工
Structured Streaming 基于 DataFrame/Dataset 的流式计算模型 实时指标、日志流处理、增量计算
MLlib Spark 内置机器学习库 特征处理、分类、回归、聚类、推荐
GraphX 图计算框架 PageRank、关系网络、连通分量
pandas API on Spark 面向 pandas 工作负载的分布式扩展 Python 数据分析迁移到大规模数据

Spark 的优势之一是多语言支持。 Spark 提供 Java、Scala、Python 和 R 的高级 API;PySpark 也支持 Spark SQL、DataFrame、Structured Streaming、MLlib 和 Spark Core 等能力。

另一个优势是部署灵活。Spark 可以本地运行用于开发测试,也可以运行在 Standalone、YARN 和 Kubernetes 上;应用提交统一使用spark-submit ,它能以统一接口适配不同 Cluster Manager 和部署模式。

sql 复制代码
Spark 功能栈简图:

+------------------------------------------------------+
|                    User APIs                         |
|      Scala / Java / Python / SQL / R                 |
+------------------------------------------------------+
| Spark SQL | Structured Streaming | MLlib | GraphX     |
+------------------------------------------------------+
|        DataFrame / Dataset / RDD                     |
+------------------------------------------------------+
|        Spark Core: DAG Scheduler / Task / Memory      |
+------------------------------------------------------+
|  Standalone / YARN / Kubernetes / Local              |
+------------------------------------------------------+
|  HDFS / S3 / Data Lake / Hive / Kafka / JDBC / Files  |
+------------------------------------------------------+

这也是 Spark 被广泛用于数据平台的原因:它把底层分布式计算复杂度隐藏起来,同时保留了足够多的性能调优入口。Spark 不只是一个"跑 SQL 的工具",也是构建数据湖、实时数仓、特征工程和离线训练管道的通用基础设施。

五、适用场景

Spark 适合数据规模较大、计算逻辑较复杂、需要分布式并行处理的场景。典型例子包括离线 ETL、宽表加工、用户画像、日志分析、数据湖表清洗、复杂 Join、机器学习特征工程,以及需要批流一体开发体验的实时指标处理。

场景 Spark 是否适合 原因
TB/PB 级离线 ETL 适合 分布式计算、SQL/DataFrame 生态成熟
复杂多表 Join 适合 可通过 Shuffle、广播 Join、分区优化处理
机器学习特征工程 适合 适合大规模数据预处理,MLlib 可覆盖部分算法
交互式探索分析 较适合 可缓存数据,但仍受集群资源和查询复杂度影响
毫秒级 OLTP 查询 不适合 Spark 不是事务型数据库
高频低延迟消息处理 视场景而定 Structured Streaming 适合微批/连续处理类场景,但极低延迟系统需评估
小数据脚本处理 通常不适合 启动和调度开销可能超过收益
作为存储系统 不适合 Spark 是计算引擎,数据仍需存储在 HDFS、对象存储、湖表或数据库中

Spark 的优势在"吞吐"和"通用性",不是所有场景都追求最低延迟。一个实用判断是:如果数据能在单机内存里轻松处理,Spark 可能过重;如果数据需要跨节点计算、需要容错、需要调度、需要与湖仓生态集成,Spark 的价值就会明显上升。

六、部署使用

理解 Spark 部署时,需要区分两个容易混淆的概念:

  • 集群管理器(Cluster Manager) :负责为 Spark 应用分配 CPU、内存和运行节点。
  • 部署模式(Deploy Mode) :决定 Driver 运行在提交客户端,还是运行在集群内部。

6.1.Cluster Manager

常见的Cluster Manager如下:

集群管理器 资源管理方式 主要特点 典型场景
Local 单进程内的多个线程 无需集群,启动简单 本地开发、单元测试、学习
Standalone Spark 自带 Master/Worker 部署简单,对 Spark 支持直接 独立 Spark 集群、中小规模平台
YARN Hadoop ResourceManager/NodeManager 与 Hadoop、HDFS 和企业权限体系结合紧密 传统 Hadoop 数据平台
Kubernetes Pod、Scheduler、容器镜像 容器化、资源隔离、云原生调度 云平台、容器化数据平台

无论使用哪种集群管理器,Spark 应用的基本运行结构都没有改变:Driver 负责协调应用,Executor 负责执行 Task 和保存缓存、Shuffle 等数据。每个 Spark Application 拥有自己独立的一组 Executor,不同应用通常不能直接共享 Executor 内存中的数据,只能通过外部存储交换数据。

markdown 复制代码
                         Spark 应用
                             │
                             │ spark-submit
                             ▼
              ┌──────────────────────────────┐
              │       选择运行环境           │
              └──────────────────────────────┘
                    │        │        │
        ┌───────────┘        │        └────────────┐
        ▼                    ▼                     ▼
┌──────────────┐    ┌────────────────┐    ┌────────────────┐
│  Standalone  │    │      YARN      │    │   Kubernetes   │
│ Master/Worker│    │ RM / NM / AM   │    │ Driver/Executor│
│              │    │                │    │      Pod       │
└──────┬───────┘    └───────┬────────┘    └───────┬────────┘
       │                    │                     │
       └────────────────────┴─────────────────────┘
                            │
                            ▼
               ┌─────────────────────────┐
               │ Driver + 多个 Executor  │
               └─────────────────────────┘

6.2.Deploy Mode

Deploy Mode 决定 Driver 的位置。

在 Client 模式下,Driver 运行在执行spark-submit 的客户端进程中,Cluster Manager 只负责在集群节点上启动 Executor。

arduino 复制代码
Client 模式

┌─────────────────────── 客户端或网关机 ──────────────────────┐
│                                                             │
│  spark-submit                                               │
│       │                                                     │
│       ▼                                                     │
│  ┌──────────────┐                                           │
│  │    Driver    │◄──────── Task 状态、结果、心跳 ───────┐   │
│  └──────┬───────┘                                       │   │
└─────────┼───────────────────────────────────────────────┼───┘
          │ 申请资源、提交 Task                           │
          ▼                                              │
┌────────────────────── 集群内部 ─────────────────────────┼───┐
│  ┌─────────────────┐                                  │   │
│  │ Cluster Manager │                                  │   │
│  └────────┬────────┘                                  │   │
│           │                                           │   │
│     ┌─────┴──────────────┐                            │   │
│     ▼                    ▼                            │   │
│ ┌────────────┐      ┌────────────┐                    │   │
│ │ Executor 1 │      │ Executor 2 │────────────────────┘   │
│ │ Task/Cache │      │ Task/Cache │                        │
│ └────────────┘      └────────────┘                        │
└─────────────────────────────────────────────────────────────┘

Client 模式的优点是交互方便,Driver 日志直接出现在提交终端,适合开发测试交互场景。

在 Cluster 模式下,提交客户端只负责发送应用;Cluster Manager 在集群内部启动 Driver,之后客户端可以退出。

arduino 复制代码
Cluster 模式

┌────────────── 提交客户端 ──────────────┐
│                                       │
│  spark-submit                         │
│       │                               │
│       └──── 提交应用后可退出           │
└───────────────┬───────────────────────┘
                │
                ▼
┌──────────────────────── 集群内部 ────────────────────────────┐
│                                                             │
│                ┌─────────────────┐                          │
│                │ Cluster Manager │                          │
│                └────────┬────────┘                          │
│                         │ 启动 Driver 和 Executor           │
│              ┌──────────┴──────────┐                       │
│              ▼                     ▼                       │
│      ┌──────────────┐      ┌───────────────────┐            │
│      │    Driver    │      │ Executor 1        │            │
│      │ DAG Scheduler│◄────►│ Task / Cache      │            │
│      └──────┬───────┘      └───────────────────┘            │
│             │                                               │
│             └─────────────►┌───────────────────┐            │
│                            │ Executor 2        │            │
│                            │ Task / Cache      │            │
│                            └───────────────────┘            │
└─────────────────────────────────────────────────────────────┘

Cluster 模式更适合生产环境,不过Cluster 模式下的 Driver 日志通常需要从 YARN、Kubernetes、Standalone Master 或日志聚合系统中查看。

6.3.spark作业提交

典型的 YARN Cluster 提交命令:

sql 复制代码
spark-submit \
  --master yarn \
  --deploy-mode cluster \
  --class com.example.UserProfileJob \
  --conf spark.executor.instances=10 \
  --conf spark.executor.cores=4 \
  --conf spark.executor.memory=8g \
  user-profile-job.jar

Master 与 Deploy Mode 的组合:

bash 复制代码
spark-submit
│
├── --master
│     ├── local[*]               本地模式
│     ├── spark://host:7077      Standalone
│     ├── yarn                   YARN
│     └── k8s://https://...      Kubernetes
│
└── --deploy-mode
      ├── client                 Driver 在提交端
      └── cluster                Driver 在集群内

七、常见问题

  • 作业为什么很慢
lua 复制代码
作业慢排查路径:

先看 Web UI
   |
   +--> Job/Stage 是否有长尾 Task
   |
   +--> Shuffle Read/Write 是否过大
   |
   +--> Executor 是否频繁 GC
   |
   +--> 是否有数据倾斜
   |
   +--> 是否资源不足或并行度过低
   |
   +--> SQL 执行计划是否出现不合理 Join / Scan

如果慢在 Shuffle,重点看 Join、GroupBy、Distinct、Window 等算子;如果慢在 GC,重点看对象数量、缓存策略、序列化方式和 Executor 内存;如果慢在单个或少数 Task,则更可能是数据倾斜或分区设计不合理。

  • 为什么会 OOM

OOM 常见于 Driver 端collect 大数据、Executor 内存不足、Shuffle 聚合过大、缓存过多、单分区数据过大或 UDF 创建大量对象。

Spark 内存主要分为执行内存和存储内存,执行内存用于 Shuffle、Join、Sort、Aggregation 等计算,存储内存用于缓存和内部数据传播;两者共享统一区域,执行内存可在必要时挤出部分存储内存。

避免 OOM 的常见方法包括:不要把大结果collect 到 Driver;控制单分区大小;减少宽依赖 Shuffle;必要时使用广播 Join;缓存后及时unpersist ;用 DataFrame 替代大量对象化 RDD;观察 Storage 页面确认缓存实际占用。

  • 并行度应该怎么设

对于每个 CPU Core,通常建议设置 2 到 3 个 Task 来提高集群利用率;如果并行度太低,集群资源无法充分使用,如果并行度过高,则调度开销和小任务数量会增加。

makefile 复制代码
并行度过低:

Core: 100
Task: 20
结果:大量 Core 空闲

并行度较合理:

Core: 100
Task: 200 ~ 300
结果:资源利用更充分,个别慢任务也更容易被调度平滑

实践中还要结合输入文件大小、Shuffle 后分区数、目标文件大小和下游系统承载能力。

  • Web UI 看什么

Spark Web UI 是排障入口,每个 Spark 应用运行期间都会提供 Web UI,默认地址通常是http://:4040 ,可用于监控运行任务、诊断失败、分析慢 SQL 执行计划,以及检查内存和任务如何分布在 Executor 上。

页面 重点观察
Jobs Job 是否失败、持续时间、触发来源
Stages Stage 耗时、Task 分布、Shuffle、失败原因
Storage 缓存数据占用、分区数量、存储级别
Environment 实际生效配置、Classpath、运行环境
Executors Executor 内存、GC、Task 数、Shuffle 读写
SQL SQL/DataFrame 查询、执行计划、算子指标
Structured Streaming 流式查询状态、批次耗时、输入速率

Web UI 不是"出问题才看"的工具,上线前通过 Web UI 观察一次作业的 Stage 划分、Shuffle 量、GC 时间和 SQL 物理计划,往往能提前发现分区过大、广播失效、扫描过多或资源配置不合理的问题。

相关推荐
七夜zippoe5 小时前
Agent 上线第一天就炸了:6 个生产事故的根因分析与修复记录
大数据·ai·agent·根因分析·修复记录
字节跳动数据平台7 小时前
不重拍,也能换道具、换商品、换画风?短剧转漫、电商换款新解法来了
大数据
武子康7 小时前
攻击动作做对了,游戏规则就对了吗?H3-World 与三条控制路线的分工
大数据·人工智能·agent
Lansonli9 小时前
大数据Spark(八十七):Spark-Submit 参数详解
大数据·分布式·spark
cc5725026539 小时前
数据科学与大数据技术专业考证清单有哪些?
大数据
喵个咪1 天前
GoWind Admin 拆解:BI 大屏设计器——schema 即 JSON、resolver 注入与三张快照表的口径谱系
大数据·后端·go
武子康1 天前
H3-World 的 11.3 小时训练数据,是怎样对齐成动作监督的?
大数据·人工智能·深度学习
字节跳动数据平台1 天前
火山引擎知识引擎VeKnow进化之路(下):让 AI 智能体更可用、更可信、更懂业务
大数据
Elastic 中国社区官方博客1 天前
更少的位数,相同的召回率:DiskBBQ 更保守的自动校准扫描
大数据·运维·开发语言·人工智能·elasticsearch·搜索引擎