手把手教你优化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不是一个大而全的「铁板一块」。它有一个核心引擎,上面挂了四个主要组件。我画了一张图,帮你理清关系:
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源码,容易劝退。