简介:spark-3.2.0-bin-hadoop3.2.tgz 是 Apache Spark 3.2.0 面向 Hadoop 3.2 环境编译的官方二进制发行包,适合大数据开发工程师、数据科学家及高校学生快速搭建分布式计算与机器学习实验环境。压缩包共 1476 个文件,约 287.02MB,以 462 个 py、238 个 jar、203 个 scala、135 个 java 等源码与依赖文件为主,并含 91 个 txt 说明、32 个 sh 与 16 个 cmd 启动脚本,以及 parquet、orc、avro 等示例数据文件,覆盖 Spark Core、SQL、Streaming、MLlib、GraphX 全部核心组件。该版本在 DataFrame/Dataset 性能、Catalyst 优化器、PySpark 一致性、Kubernetes 原生支持及内存管理上均有改进,并新增时间旅行查询等能力。解压后可直接在 Hadoop 3.2 集群运行 spark-submit、spark-shell 与 pyspark,便于对照源码与脚本理解任务调度、SQL 执行及流处理机制。目前已有 1123 人学习下载,适合需要完整发行包进行本地调试、集群部署与版本对比的读者。
1. 拿到 spark-3.2.0-bin-hadoop3.2.tgz 之后:它到底是什么,能省掉哪些折腾
如果你在搜索框里敲下spark-3.2.0-bin-hadoop3.2.tgz,大概率不是想听 Spark 的发展史,而是手里已经有一个 tgz 包,或者正准备下载一个,想知道它能不能直接跑、跟 Hadoop 怎么对接、解压完该改哪几个文件。这个包是 Apache Spark 官方发布的预编译二进制发行版,版本号 3.2.0,编译时绑定的 Hadoop 版本是 3.2。名字里的bin说明它已经帮你把 Scala、Java 依赖、启动脚本、示例 jar 全部打好了,hadoop3.2说明它默认按 Hadoop 3.2 的客户端接口来编译,连的是 Hadoop 3.x 的 HDFS 和 YARN。
它解决的核心问题是:你不需要自己装 Maven、不需要从源码编译、不需要折腾 Scala 版本对齐,解压就能用spark-shell、spark-submit、pyspark。适合谁?适合要搭本地开发环境的后端或数据开发、要跑课程设计的学生、要在伪分布式或集群上提交第一个 Spark 作业的人。不适合谁?不适合已经用 Databricks 托管服务、或者需要 Spark 3.5 新特性的人。一句话:这是「从零开始安装 Hadoop」之后,最省事的那块拼图。
2. 解压、环境变量与三种启动模式:把 tgz 变成能跑的命令
2.1 解压位置和目录结构决定了后面所有路径
拿到 tgz 之后第一件事不是急着spark-shell,而是选一个不会带空格、不会带中文的路径。Linux 下我一般放/opt/bigdata/,Windows 下放D:\bigdata\。原因很简单:Spark 的启动脚本里大量拼接路径,空格会让classpath断掉,中文在某些 locale 下会乱码。
# Linux / macOS 下解压,注意 -C 指定目标目录 mkdir -p /opt/bigdata tar -zxvf spark-3.2.0-bin-hadoop3.2.tgz -C /opt/bigdata/ # 进入目录看结构,确认关键子目录都在 cd /opt/bigdata/spark-3.2.0-bin-hadoop3.2 ls -1 # bin/ 启动脚本:spark-shell、spark-submit、pyspark # sbin/ 集群脚本:start-master.sh、start-workers.sh # conf/ 配置文件:spark-env.sh、spark-defaults.conf # jars/ 所有依赖 jar,Spark 本体和 Hadoop 客户端都在这里 # examples/ 示例 jar,用来验证环境 # python/ pyspark 的 Python 包逻辑说明:bin/是给开发者用的,sbin/是给集群管理员用的。jars/里已经包含了 Hadoop 3.2 的客户端 jar,所以只要你连的 HDFS 是 3.x,理论上不用额外拷 jar。参数说明:-C指定解压目录,-z表示 gzip,-x解压,-v显示过程,-f指定文件。这四个参数顺序可以换,但-f必须紧挨文件名。
2.2 环境变量只配三个,多配反而容易冲突
很多人一上来就抄一堆SPARK_HOME、HADOOP_HOME、JAVA_HOME、SCALA_HOME,结果spark-submit报No such file or directory。血泪经验是:Spark 3.2 自带 Scala,不需要你单独装 Scala;Hadoop 客户端 jar 已经在jars/里,不需要HADOOP_HOME也能跑本地模式。真正必须配的只有JAVA_HOME和SPARK_HOME,PATH里加上$SPARK_HOME/bin。
# 编辑 ~/.bashrc 或 /etc/profile export JAVA_HOME=/usr/lib/jvm/java-8-openjdk-amd64 export SPARK_HOME=/opt/bigdata/spark-3.2.0-bin-hadoop3.2 export PATH=$PATH:$SPARK_HOME/bin:$SPARK_HOME/sbin # 让配置生效 source ~/.bashrc # 验证:能打印版本就说明环境变量对了 spark-submit --version逻辑说明:spark-submit --version会输出 Spark 版本、Scala 版本、Java 版本。如果这里报JAVA_HOME is not set,说明 Java 路径写错了。参数说明:Java 8 是 Spark 3.2 的推荐版本,Java 11 也能跑但部分 API 有警告,Java 17 不建议。SPARK_HOME不要带结尾斜杠,脚本里会自己加。
2.3 三种启动模式:local、standalone、on YARN 怎么选
Spark 3.2 的spark-submit支持多种 master URL,新手最容易懵的是local[*]、spark://host:7077、yarn到底什么时候用哪个。
| master URL | 适用场景 | 是否需要 Hadoop | 进程数 |
|---|---|---|---|
local[2] | 本地开发、单元测试 | 否 | 单 JVM 内 2 线程 |
local[*] | 本地跑小数据集 | 否 | 用满所有核 |
spark://host:7077 | 独立集群,不依赖 YARN | 否 | Master + Worker |
yarn | 已有 Hadoop 集群,资源统一调度 | 是 | YARN 分配 |
常见做法是:开发阶段用local[*],验证逻辑;要连 HDFS 读数据时,把fs.defaultFS指向你的 NameNode;要提交到集群时,再换成yarn。注意local[*]下sc.textFile("hdfs://...")也能跑,因为 Spark 会直接用jars/里的 Hadoop 客户端去连,不需要你启动 YARN。
# 本地模式启动 spark-shell,验证安装 spark-shell --master local[2] # 在 shell 里跑一行 WordCount,确认能出结果 val rdd = sc.parallelize(List("spark", "hadoop", "spark")) rdd.map((_, 1)).reduceByKey(_ + _).collect() # 预期输出:Array((spark,2), (hadoop,1))逻辑说明:sc是 SparkContext,spark-shell启动时自动创建。parallelize把本地集合转成 RDD,reduceByKey做聚合。参数说明:local[2]表示用 2 个线程,写local[*]会用满 CPU 核数。这一步能跑通,说明 tgz 包本身没问题,后面连不上 HDFS 就是 Hadoop 侧的事。
3. 连 HDFS 与提交 YARN:配置文件改哪几行,参数怎么对齐
3.1 让 Spark 找到 HDFS:core-site.xml 的两种放法
Spark 读 HDFS 靠的是 Hadoop 客户端配置。spark-3.2.0-bin-hadoop3.2.tgz的jars/里已经有hadoop-client相关 jar,但默认不知道你的 NameNode 地址。两种做法:一是把 Hadoop 的core-site.xml和hdfs-site.xml拷到$SPARK_HOME/conf/,二是直接在spark-defaults.conf里写spark.hadoop.fs.defaultFS。我一般用第一种,因为跟 Hadoop 命令行行为一致,排查时少一个变量。
# 假设 Hadoop 装在 /opt/bigdata/hadoop-3.2.1 cp /opt/bigdata/hadoop-3.2.1/etc/hadoop/core-site.xml $SPARK_HOME/conf/ cp /opt/bigdata/hadoop-3.2.1/etc/hadoop/hdfs-site.xml $SPARK_HOME/conf/ # 确认 core-site.xml 里的 fs.defaultFS 指向你的 NameNode grep -A1 "fs.defaultFS" $SPARK_HOME/conf/core-site.xml # 常见值:hdfs://localhost:9000 或 hdfs://namenode-host:8020逻辑说明:Spark 启动时会自动加载conf/下的core-site.xml,所以拷过来就能生效。参数说明:fs.defaultFS的端口取决于 Hadoop 配置,伪分布式常见 9000 或 8020,集群模式看core-site.xml实际值。如果这里写错,spark-shell里执行sc.textFile("hdfs:///tmp/test.txt")会报Connection refused。
# 验证:在 spark-shell 里读 HDFS 文件 spark-shell --master local[2] val data = sc.textFile("hdfs://localhost:9000/tmp/wordcount.txt") data.count() # 能返回行数说明 HDFS 连通逻辑说明:textFile返回 RDD[String],count()触发实际读取。如果报FileNotFoundException,说明路径不对;报Connection refused,说明 NameNode 地址或端口不对。注意 HDFS 上文件路径要写全,/tmp/wordcount.txt前面要加hdfs://localhost:9000。
3.2 提交到 YARN:spark-submit 的五个关键参数
当你要把作业扔到已有 Hadoop 集群上跑,--master yarn是入口,但真正决定成败的是--deploy-mode、--executor-memory、--executor-cores、--num-executors、--queue这几个。Hadoop 作业提交到 YARN 的流程里,Spark 会先申请一个 ApplicationMaster 容器,再由 AM 去申请 Executor 容器。
# 提交一个 Spark 作业到 YARN,client 模式便于看日志 spark-submit \ --master yarn \ --deploy-mode client \ --executor-memory 2g \ --executor-cores 2 \ --num-executors 4 \ --queue default \ --class org.apache.spark.examples.SparkPi \ $SPARK_HOME/examples/jars/spark-examples_2.12-3.2.0.jar \ 100逻辑说明:--class指定主类,最后两个参数是 jar 路径和传给 main 的参数。--deploy-mode client下 Driver 跑在你提交命令的机器上,日志直接打印在终端,适合调试;cluster模式下 Driver 跑在 YARN 容器里,终端看不到日志,要用yarn logs -applicationId查。参数说明:--executor-memory 2g是每个 Executor 的内存,--executor-cores 2是每个 Executor 的核数,--num-executors 4是 Executor 总数。总资源 = 4 * 2g 内存 + 4 * 2 核,要小于队列可用资源,否则会卡在ACCEPTED状态。
注意:
--num-executors在--deploy-mode client下由spark-submit直接传给 YARN,但在某些 Hadoop 版本里会被spark.dynamicAllocation覆盖。如果发现 Executor 数量不对,先检查spark-defaults.conf里有没有开启动态分配。
3.3 读 JSON 和写 Parquet:Spark 3.2 里最常用的两个数据入口
Spark 数据分析案例里,读 JSON 和写 Parquet 几乎是标配。Spark 3.2 的spark.read.json支持多行 JSON,但默认只认单行一条记录。如果你的 JSON 文件是格式化过的多行结构,要加multiLine=true。
# pyspark 读 JSON,注意 multiLine 参数 from pyspark.sql import SparkSession spark = SparkSession.builder \ .appName("json-demo") \ .master("local[2]") \ .getOrCreate() # 单行 JSON,直接读 df = spark.read.json("hdfs://localhost:9000/tmp/users.json") df.printSchema() df.show(5) # 多行 JSON,必须加 option df_multi = spark.read.option("multiLine", "true").json("hdfs://localhost:9000/tmp/nested.json") # 写成 Parquet,压缩用 snappy df.write.mode("overwrite").parquet("hdfs://localhost:9000/tmp/users_parquet")逻辑说明:printSchema()打印推断出的 schema,show(5)显示前 5 行。multiLine=true会让 Spark 先读整个文件再解析,大文件慎用,容易 OOM。参数说明:mode("overwrite")覆盖已有目录,mode("append")追加。Parquet 默认压缩是 snappy,如果集群没装 snappy 本地库,会报native snappy library not available,这时可以换gzip或none。
# 如果 snappy 报错,改用 gzip df.write.mode("overwrite").option("compression", "gzip").parquet("hdfs://localhost:9000/tmp/users_parquet_gz")逻辑说明:option("compression", "gzip")在写 Parquet 时生效。参数说明:gzip 压缩率高但读写慢,snappy 平衡最好,lz4 更快但压缩率低。生产环境一般用 snappy,本地测试没装 native 库时用 gzip 绕过。
4. 避坑与排查:tgz 包跑不起来时先看这五条
4.1 现象:spark-shell启动报JAVA_HOME is not set
原因:spark-env.sh或系统环境变量里JAVA_HOME没配,或者配了但路径指向 JRE 而不是 JDK。Spark 需要 JDK 里的tools.jar(Java 8)或jdk.compiler模块(Java 11+)。
解决:确认echo $JAVA_HOME输出的是 JDK 根目录,且$JAVA_HOME/bin/javac存在。如果用的是 Java 11,Spark 3.2 能跑但会有Illegal reflective access警告,不影响功能。Java 17 需要加--add-opens参数,不建议新手折腾。
4.2 现象:连 HDFS 报Connection refused或UnknownHostException
原因:core-site.xml里的fs.defaultFS写的是主机名,但本机/etc/hosts没解析;或者 NameNode 根本没启动。
解决:先用hdfs dfs -ls /确认 Hadoop 自己能连上。如果 Hadoop 命令能连、Spark 连不上,检查$SPARK_HOME/conf/下有没有core-site.xml。如果报UnknownHostException,在/etc/hosts里加一行127.0.0.1 namenode-host。
4.3 现象:提交 YARN 后一直卡在ACCEPTED,不变成RUNNING
原因:YARN 队列资源不足,或者--executor-memory加--executor-cores算出来的总资源超过了队列上限。常见于--num-executors设太大。
解决:用yarn application -list看应用状态,用yarn queue -status default看队列资源。先把--num-executors降到 1、--executor-memory降到 1g 试跑,能跑通再往上加。注意spark.yarn.am.memory默认 1g,如果 AM 都申请不到,会直接失败。
4.4 现象:读 JSON 报Failed to read data或 schema 全变成 string
原因:JSON 文件里有格式不一致的行,或者multiLine没开。Spark 的 schema 推断会扫描一部分数据,如果样本里某字段全是 null,推断出来就是 string。
解决:先df.printSchema()看推断结果,如果不对,手动指定 schema。用spark.read.schema(customSchema).json(...)跳过推断。对于多行 JSON,加option("multiLine", "true"),但注意大文件会内存溢出。
4.5 现象:写 Parquet 报native snappy library not available
原因:spark-3.2.0-bin-hadoop3.2.tgz自带的 snappy 是 Java 实现,但某些操作会尝试加载 native 库。在 Windows 或精简版 Linux 上容易触发。
解决:写的时候显式指定option("compression", "gzip")或option("compression", "none")。如果一定要 snappy,在spark-defaults.conf里加spark.sql.parquet.compression.codec=gzip全局改掉。这个坑在课程设计里很常见,因为学生机往往没装libsnappy。
5. 进阶技巧:用 spark-submit 的 --files 和 --jars 把依赖带进集群
5.1 为什么需要 --files 和 --jars
本地跑得好好的作业,一提交到 YARN 就报ClassNotFoundException或FileNotFoundException,八成是依赖没带上去。spark-submit的--jars把额外 jar 分发到每个 Executor 的 classpath,--files把配置文件、字典文件分发到每个 Executor 的工作目录。这两个参数是 Spark 作业从「本地能跑」到「集群能跑」的关键一步。
# 提交时带上自定义 jar 和配置文件 spark-submit \ --master yarn \ --deploy-mode cluster \ --jars /opt/libs/mysql-connector-java-8.0.28.jar,/opt/libs/fastjson-1.2.79.jar \ --files /opt/conf/app.properties,/opt/data/dict.txt \ --executor-memory 4g \ --executor-cores 2 \ --num-executors 8 \ --class com.example.MainJob \ /opt/jobs/my-spark-job.jar \ --input hdfs://localhost:9000/tmp/input \ --output hdfs://localhost:9000/tmp/output逻辑说明:--jars里的 jar 会被复制到每个 Executor 的$PWD下并加入 classpath。--files里的文件会被复制到每个 Executor 的工作目录,代码里用new File("app.properties")就能读到,不需要写绝对路径。参数说明:多个 jar 用逗号分隔,不要有空格。--files同理。注意--jars里的 jar 如果跟 Spark 自带 jar 冲突,会优先用--jars里的,这可能导致版本问题。
5.2 在代码里读取 --files 分发的文件
# pyspark 里读取 --files 分发的配置文件 from pyspark import SparkFiles # 方式一:通过 SparkFiles.get 拿绝对路径 config_path = SparkFiles.get("app.properties") with open(config_path) as f: content = f.read() # 方式二:直接在当前工作目录找(Executor 的 CWD 就是分发目录) import os if os.path.exists("dict.txt"): with open("dict.txt") as f: dict_data = f.read().splitlines()逻辑说明:SparkFiles.get("app.properties")返回该文件在 Executor 上的绝对路径,Driver 和 Executor 都能用。方式二依赖 Executor 的 CWD,在cluster模式下更可靠。参数说明:--files分发的文件默认不覆盖同名文件,如果 Executor 上已有同名文件,行为取决于 Spark 版本,3.2 里会覆盖。
5.3 验证依赖是否真的分发成功
提交之后不要只看ApplicationMaster状态,要进 Executor 日志确认。用yarn logs -applicationId application_xxx拉日志,搜app.properties或mysql-connector。如果日志里报FileNotFoundException: app.properties,说明--files没生效,检查路径是不是绝对路径、文件是不是存在。
# 拉取 YARN 应用日志,过滤关键信息 yarn logs -applicationId application_1680000000000_0001 | grep -E "app.properties|mysql-connector|ClassNotFoundException"逻辑说明:yarn logs会把 AM 和所有 Executor 的日志合并输出,grep过滤出跟依赖相关的行。参数说明:applicationId从yarn application -list里拿。如果日志太大,可以加-log_files指定只拉某个容器的日志。
从那以后我每次提交集群作业前,都强制走一遍「本地local[2]跑通 → 带--jars和--files提交yarn client→ 确认日志无ClassNotFoundException→ 再换cluster模式」这个流程。多花五分钟,省掉半夜被电话叫醒查 YARN 日志的麻烦。希望帮到你。
本文还有配套的精品资源,点击获取