2. 开发环境搭建:本地模式安装、Spark-Shell初体验、第一个WordCount程序
说实话,很多初学者一上来就搞集群,结果环境配了三天,连个 Hello World 都没跑起来。我个人习惯是:先本地跑通,再上集群。本地模式虽然简单,但能让你快速理解 Spark 的核心概念。今天我们就从零开始,把环境搭好,再写个 WordCount 练练手。
2.1 本地模式安装
本地模式说白了就是 Spark 不需要集群,直接在单机上跑。它把所有进程都塞进一个 JVM 里,适合开发和调试。我在项目中经常用它来快速验证逻辑,等确认没问题了再丢到集群上跑。
2.1.1 前置条件
先确认你的机器上已经装好了 Java 8 或 11。Spark 3.x 对 Java 版本有要求,我建议直接用 Java 8,兼容性最好。
# 检查 Java 版本
java -version
# 输出示例(你的版本号可能不同)
# java version "1.8.0_291"
嗯,这里要注意:Spark 3.2 之后开始支持 Java 17,但我个人不建议在生产环境用太新的版本,踩坑概率大。
2.1.2 下载与解压
去 Apache Spark 官网下载预编译好的包。选的时候注意看 Hadoop 版本,如果你本地没装 Hadoop,选 "Pre-built for Apache Hadoop 3.2 and later" 就行。
# 下载 Spark 3.3.0(以这个版本为例)
wget https://archive.apache.org/dist/spark/spark-3.3.0/spark-3.3.0-bin-hadoop3.tgz
# 解压
tar -zxvf spark-3.3.0-bin-hadoop3.tgz
# 移动到常用目录
sudo mv spark-3.3.0-bin-hadoop3 /usr/local/spark
2.1.3 配置环境变量
编辑 ~/.bashrc 或 ~/.zshrc,加上下面几行:
export SPARK_HOME=/usr/local/spark
export PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin
然后 source ~/.bashrc 让它生效。验证一下:
spark-shell --version
如果能看到版本信息,恭喜你,环境搭好了。
$SPARK_HOME/conf 目录下创建一个 spark-defaults.conf 文件,里面写上 spark.master local[*]。这样每次启动 Spark 应用时,默认就是本地模式,省得每次都要指定。
2.2 Spark-Shell 初体验
Spark-Shell 是 Spark 提供的交互式命令行工具。说白了,它就是一个 Scala REPL 环境,里面自动帮你创建好了 SparkContext 和 SparkSession。你想想看,不用写 main 方法,不用编译打包,直接敲代码就能跑,多爽。
2.2.1 启动 Spark-Shell
在终端里直接输入:
spark-shell
你会看到一堆日志输出,最后出现 scala> 提示符。启动时默认使用本地模式,使用 2 个线程(一个用于调度,一个用于执行)。
如果你想指定更多资源,可以这样:
spark-shell --master local[4] --executor-memory 2g
这表示用 4 个线程、每个执行器 2GB 内存。我在本地调试时一般给 2-4 个线程,再多也没意义,毕竟笔记本就那点资源。
2.2.2 第一个 Spark 操作
进入 Spark-Shell 后,试试这个:
val data = 1 to 10
val rdd = sc.parallelize(data)
rdd.collect()
你会看到输出 Array[Int] = Array(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)。这里 sc 就是 SparkContext,Spark-Shell 启动时已经帮你创建好了。
2.3 第一个 WordCount 程序
WordCount 是大数据领域的 "Hello World"。虽然简单,但它涵盖了 Spark 编程的核心流程:读取数据 → 转换处理 → 输出结果。我在面试新人时,经常用这个来考察对方对 Spark 基础 API 的掌握程度。
2.3.1 准备数据文件
先创建一个文本文件,里面随便写几行英文:
echo "hello world hello spark" > /tmp/words.txt
echo "spark is awesome spark is fast" >> /tmp/words.txt
echo "hello spark world" >> /tmp/words.txt
2.3.2 在 Spark-Shell 中运行 WordCount
在 Spark-Shell 中逐行输入以下代码:
// 1. 读取文件,生成 RDD
val lines = sc.textFile("/tmp/words.txt")
// 2. 将每行拆分成单词
val words = lines.flatMap(line => line.split(" "))
// 3. 将每个单词映射成 (word, 1) 键值对
val wordPairs = words.map(word => (word, 1))
// 4. 按单词分组并求和
val wordCounts = wordPairs.reduceByKey((a, b) => a + b)
// 5. 收集结果并打印
wordCounts.collect().foreach(println)
输出结果应该是:
(hello,2)
(world,2)
(spark,3)
(is,2)
(awesome,1)
(fast,1)
你看,整个过程不到 10 行代码。这就是 Spark 的魅力——简洁、高效。
2.3.3 代码逐行解析
| 步骤 | 代码 | 说明 |
|---|---|---|
| 1 | sc.textFile(...) |
读取文件,返回 RDD[String],每行一个元素 |
| 2 | flatMap(...) |
将每行按空格拆分,展平成一个单词列表 |
| 3 | map(...) |
将每个单词转为 (word, 1) 键值对 |
| 4 | reduceByKey(...) |
按 key 分组,对 value 求和 |
| 5 | collect() |
将结果从各分区收集到 Driver 端,打印输出 |
collect() 会把所有数据拉到 Driver 端。如果数据量很大,会导致 Driver OOM。我曾经在线上环境看到有人对几十 GB 的数据用 collect(),结果 Driver 直接挂了。生产环境建议用 saveAsTextFile() 或 take(N) 来查看部分数据。
2.4 本章知识体系
下面这张图帮你梳理了本章的核心脉络:
2.5 避坑指南
最后分享几个我踩过的坑:
- 路径问题:
sc.textFile()默认读取 HDFS 路径。如果你在本地模式想读本地文件,一定要用file://前缀,比如sc.textFile("file:///tmp/words.txt")。我曾经忘了加这个前缀,结果一直报文件找不到,折腾了半小时才发现。 - 日志刷屏: Spark-Shell 启动时会输出大量 INFO 日志。你可以把
$SPARK_HOME/conf/log4j.properties.template复制为log4j.properties,然后把log4j.rootCategory=INFO, console改成log4j.rootCategory=WARN, console,这样日志就清爽多了。 - 内存不足: 本地模式默认使用 1GB 内存。如果你的机器内存不大,启动时加上
--driver-memory 512m限制一下,不然跑大一点的数据集可能会卡死。
好了,环境搭好了,第一个程序也跑通了。接下来你就可以放心大胆地折腾 Spark 了。记住,本地模式是你最好的朋友——快速验证、及时反馈,比在集群上反复提交作业高效得多。