手把手教你优化Spark作业 - 第1节

一、初识Spark:它到底是什么?

我第一次接触Spark是在2015年,当时公司有个离线报表任务,用Hadoop跑要40分钟。换了Spark之后,同样的逻辑,6分钟就跑完了。说实话,当时我挺震惊的。

Spark是一个通用分布式计算引擎。它不存数据(那是HDFS的事),也不管资源调度(那是YARN/K8s的事)。它的核心能力就一个:把计算任务拆成小份,扔到集群里并行跑。

一句话总结:Spark = 内存计算 + 统一API + 多模态处理。

二、为什么Spark比Hadoop快?

这个问题面试常问,也是很多新人容易搞混的地方。我直接说结论:不是Hadoop慢,是Spark的设计更聪明。

2.1 内存计算 vs 磁盘落地

Hadoop MapReduce每个stage都要把中间结果写到磁盘。比如一个Join操作,Map写完磁盘,Reduce再从磁盘读。磁盘I/O是瓶颈,你想想看,机械硬盘的寻道时间比内存慢几个数量级。

Spark不一样。它尽量把中间结果留在内存里。只有内存不够了,才 spill 到磁盘。这个差别,在迭代计算(比如机器学习)里特别明显。

对比项 Hadoop MapReduce Spark
中间结果存储 磁盘(每次shuffle写盘) 内存优先
迭代计算 每轮都要读写磁盘 DAG复用中间结果
启动开销 每个Task启动JVM Executor常驻
编程模型 Map + Reduce 两阶段 DAG + RDD算子

2.2 DAG调度引擎

Hadoop的MapReduce只有两个阶段:Map和Reduce。复杂逻辑要串成多个Job,每个Job都要写磁盘。

Spark引入了DAG(有向无环图)调度器。它会分析你的代码,把多个操作合并成一个Stage。比如你做了map、filter、map、reduceByKey,Spark会优化成:

  • Stage 1:map + filter + map(流水线执行,不落盘)
  • Stage 2:shuffle + reduceByKey

我遇到过不少新人,写了十几个map和filter,担心性能。其实Spark会自动pipeline,你只管写逻辑,它管优化。

2.3 细粒度容错

Hadoop容错靠备份执行(Speculative Execution)。一个Task慢了,就再起一个副本。浪费资源。

Spark容错靠RDD的血统(Lineage)。某个分区丢了?从父RDD重新计算就行。不需要全量重跑。这个设计,说白了就是「谁出问题谁负责」,而不是「大家一起重来」。

我的经验:Spark在数据量10TB以下,性能优势非常明显。超过100TB,磁盘I/O和网络带宽会成为瓶颈,这时候需要调优shuffle和序列化。后面章节我会详细讲。

三、Spark核心组件概览

Spark不是一个大而全的「铁板一块」。它有一个核心引擎,上面挂了四个主要组件。我画了一张图,帮你理清关系:

Spark 核心组件架构 Spark Core(核心引擎) Spark SQL Spark Streaming MLlib GraphX 说明: • Spark Core 提供内存计算、任务调度、容错等基础能力 • 四个组件共享同一套核心引擎,可以混合使用 • 比如:Streaming 中调用 SQL 做窗口聚合,MLlib 中读取 DataFrame

3.1 Spark Core — 一切的基础

Spark Core提供了:

  • RDD:弹性分布式数据集,Spark的「最小计算单元」
  • 调度器:DAGScheduler + TaskScheduler
  • 存储管理:内存/磁盘/堆外内存
  • 容错机制:Lineage + Checkpoint

我建议初学者先搞懂RDD的三个特性:分区、不可变、可并行操作。搞懂了这些,后面学DataFrame、Dataset就顺了。

3.2 Spark SQL — 用得最多的组件

Spark SQL让你用SQL查数据,也提供了DataFrame/Dataset API。我个人习惯:能用SQL写的,绝不用RDD。为什么?

  • SQL有Catalyst优化器,自动做谓词下推、列剪枝
  • SQL可读性强,团队协作成本低
  • SQL + DataFrame 性能通常优于手写RDD

注意:Spark SQL不是「把SQL翻译成MapReduce」。它有自己的执行引擎,叫Tungsten,做了代码生成和内存管理优化。所以同样的SQL,Spark比Hive快很多。

3.3 Spark Streaming — 准实时流处理

Spark Streaming的核心思想是微批处理(Micro-Batch)。把流数据切成小批次,每个批次当成一个RDD来处理。

举个例子:

// 从Kafka读取流数据,每5秒一个批次
val stream = SparkSession.builder()
  .appName("StreamingExample")
  .getOrCreate()
  .readStream
  .format("kafka")
  .option("subscribe", "topic")
  .load()

// 做聚合,输出到控制台
stream.selectExpr("CAST(value AS STRING)")
  .writeStream
  .outputMode("append")
  .format("console")
  .start()
  .awaitTermination()

我曾经用Spark Streaming做过一个实时风控系统,延迟控制在10秒以内。注意,它不是真正的「实时」,是「近实时」。如果你需要毫秒级延迟,可以考虑Flink。但大多数业务场景,5-10秒的延迟完全够用。

3.4 MLlib — 分布式机器学习库

MLlib提供了常见的机器学习算法:

  • 分类:逻辑回归、决策树、随机森林
  • 聚类:K-Means、LDA
  • 推荐:ALS(交替最小二乘法)
  • 特征工程:TF-IDF、Word2Vec、StandardScaler

说实话,MLlib的算法深度不如scikit-learn。但它的优势是能处理TB级数据。我做过一个用户画像项目,数据量200亿条,单机sklearn直接OOM。换成MLlib的LogisticRegression,集群上跑了2小时就出结果了。

3.5 GraphX — 图计算引擎

GraphX做图计算,比如PageRank、连通分量、最短路径。用得相对少一些,但在社交网络分析、知识图谱场景下很实用。

举个例子,计算一个社交网络里所有用户的PageRank:

val graph = GraphLoader.edgeListFile(sc, "hdfs://edges.txt")
val ranks = graph.pageRank(0.0001).vertices
ranks.saveAsTextFile("hdfs://ranks")

嗯,这里要注意:GraphX的API比较底层,学习曲线陡。如果你只是做简单的图分析,可以考虑用Spark SQL的递归CTE代替。

四、什么时候用Spark?

不是所有场景都适合Spark。我列了个清单,帮你快速判断:

场景 推荐 原因
ETL、数据清洗 ✅ Spark SQL SQL简洁,Catalyst优化好
离线报表、批处理 ✅ Spark Core/SQL 比Hive快3-10倍
实时风控、监控 ✅ Spark Streaming 秒级延迟,生态成熟
大规模机器学习 ✅ MLlib 分布式训练,支持TB级数据
毫秒级实时计算 ❌ 考虑Flink Spark微批延迟>1秒
小数据量(<1GB) ❌ 用Pandas Spark启动开销大,杀鸡用牛刀

我的建议:如果你刚接触Spark,先从Spark SQL入手。它最容易上手,也最能解决实际问题。等SQL不够用了,再学RDD和底层调优。别一上来就啃RDD源码,容易劝退。