diff --git a/README.md b/README.md index 7f7b528..1e18e4a 100644 --- a/README.md +++ b/README.md @@ -1,179 +1,753 @@ -# Spark 学习项目(spark_project) +# Spark 学习项目 -跟着 ChatGPT 课程的 Spark 学习笔记,用 **Scala + Java 双实现** 覆盖 Spark Core 的 RDD 基础、PairRDD、WordCount,并通过 MySQL 写入的示例表把练习串成一个迷你数仓案例。 +基于 **Scala + Java** 的 Apache Spark 学习与练习项目。 -> 课程三讲(一→三)一→一对应当前 `src/main/scala/org/exercises/exercises{1,2,3}` 三套练习;另有「线下课:RDD 创建/操作/重分区」的一份 Java 版 Demo。 +本项目主要用于个人学习和记录 Spark,从 Spark 基础、RDD、PairRDD、Shuffle、Partition 逐步深入,同时保留线下课程中的 DataFrame、缓存、Checkpoint、YARN 提交等示例代码。 + +项目以 **Scala 为主,Java 为辅**,代码按照课程进行拆分,每一课尽量保持独立,方便学习、复习和实验。 + +--- + +## 项目目标 + +通过循序渐进的练习掌握 Spark Core 的核心概念,并逐步过渡到 Spark SQL。 + +学习重点包括: + +- Spark 应用程序的基本结构 +- `SparkConf`、`SparkContext` +- RDD 的创建、转换和行动 +- `map`、`filter`、`flatMap`、`reduce`、`foreach` +- PairRDD 与 `reduceByKey` +- `groupByKey` 与 `reduceByKey` +- Shuffle 的基本原理 +- RDD Partition 分区 +- `glom`、`repartition`、`coalesce` +- `partitionBy` +- 自定义 `Partitioner` +- WordCount +- Spark SQL / DataFrame +- Cache、Persist、Checkpoint +- Spark 程序打包及 YARN 提交 + +--- ## 技术栈 -| 类别 | 版本 | -| ---------- | ------------------------------------------ | -| JVM | Java 8 | -| 语言 | Scala 2.11.12 + Java 8(双实现) | -| 计算引擎 | Apache Spark 2.4.3(core + sql) | -| 分布式 | Hadoop Client 2.7.3 | -| 数据库 | MySQL 8.0.28 | -| 连接池 | HikariCP 4.0.3 | -| JDBC 工具 | commons-dbutils 1.6 | -| 测试 | JUnit 3.8.1 | -| 构建 | Maven 3 + scala-maven-plugin 4.8.1 + maven-assembly-plugin 3.7.1 | +| 技术 | 版本 / 说明 | +| --- | --- | +| JDK | 8 | +| Scala | 2.11.12 | +| Apache Spark | 2.4.3 | +| Hadoop Client | 2.7.3 | +| MySQL | 8.0.28 | +| HikariCP | 4.0.3 | +| commons-dbutils | 1.6 | +| JUnit | 3.8.1 | +| 构建工具 | Maven 3 | +| Scala Maven Plugin | 4.8.1 | +| Maven Assembly Plugin | 3.7.1 | + +项目 `pom.xml` 当前固定使用 **Java 8 + Scala 2.11.12 + Spark 2.4.3**,因此建议使用对应版本的 JDK 和依赖环境。 + +--- ## 项目结构 -``` -spark_project/ -├── pom.xml # Maven 配置(强制 JDK 8 / Scala 2.11) -├── src/main/java/org/example/ -│ ├── core/ -│ │ ├── GroupByKeyDemo.java # Java 版 4 个核心 Demo -│ │ ├── MyPartitioner.java -│ │ ├── RddCreateDemo.java -│ │ └── RepartitionVsCoalesce.java -│ └── AppTest.java # JUnit 默认骨架 -├── src/main/scala/org/example/ -│ ├── App.scala # Scala 入口:local[2] 模式 + word.txt WordCount -│ ├── test.scala -│ └── core/ # Scala 版同名 4 个 Demo -│ ├── GroupByKeyDemo.scala -│ ├── MyPartitioner.scala -│ ├── RddCreateDemo.scala -│ └── RepartitionVsCoalesce.scala -├── src/main/scala/org/exercises/ -│ ├── exercises1/ # 第 1 课:Spark 入门 -│ │ ├── demo/ # 演示 Demo1~5 -│ │ └── exercises/Exercises1..5.scala # 课后 5 题(题目以中文注释形式嵌入文件头) -│ ├── exercises2/ # 第 2 课:RDD 算子(map / filter / flatMap / reduce / foreach) -│ └── exercises3/ # 第 3 课:PairRDD / reduceByKey / WordCount -└── src/main/resources/ - ├── create_table.sql # MySQL:Top10 平均评分电影 + 类别平均评分 - ├── log4j.properties - └── word.txt +```text +spark_20260915/ +├── pom.xml +├── README.md +├── AGENTS.md +│ +├── src/ +│ ├── main/ +│ │ ├── java/ +│ │ │ └── org/example/ +│ │ │ └── core/ +│ │ │ ├── GroupByKeyDemo.java +│ │ │ ├── MyPartitioner.java +│ │ │ ├── RddCreateDemo.java +│ │ │ └── RepartitionVsCoalesce.java +│ │ │ +│ │ ├── scala/ +│ │ │ └── org/ +│ │ │ ├── example/ +│ │ │ │ ├── App.scala +│ │ │ │ ├── test.scala +│ │ │ │ ├── core/ +│ │ │ │ └── sql/ +│ │ │ │ +│ │ │ └── exercises/ +│ │ │ ├── exercises1/ +│ │ │ ├── exercises2/ +│ │ │ ├── exercises3/ +│ │ │ ├── exercises4/ +│ │ │ └── exercises5/ +│ │ │ +│ │ └── resources/ +│ │ ├── log4j.properties +│ │ ├── word.txt +│ │ └── create_table.sql +│ │ +│ └── test/ +│ └── java/ +│ +└── target/ ``` -## 快速上手 +### 目录说明 -### 1. 准备环境 +#### `org/example` -- **JDK 8**(`pom.xml` 已锁定 `maven.compiler.source/target=8`,高版本会编译失败) -- **Maven 3.6+**(仓库根目录留有 `.mvn/`,但还没生成 `mvnw`,自行安装一个 `mvn` 到 `PATH` 即可) -- 一台能跑 `local` / `local[2]` 模式的机器即可,**不需要真正的 Spark 集群** +主要存放课程外的 Demo、线下课程代码以及一些完整示例。 -### 2. 编译与打包 +其中: + +```text +org/example/core/ +``` + +主要用于演示 Spark Core 基础功能,包括: + +- RDD 创建 +- `groupByKey` +- 自定义 `Partitioner` +- `repartition` +- `coalesce` + +同时存在 Java 与 Scala 两套实现,用于对比两种语言编写 Spark 程序的方式。 + +```text +org/example/sql/ +``` + +用于存放 Spark SQL / DataFrame 相关示例,例如通过 `SparkSession` 创建 DataFrame。 + +--- + +#### `org/exercises` + +这是本项目最核心的学习目录。 + +每一课按照课程编号划分: + +```text +exercises1 +exercises2 +exercises3 +exercises4 +exercises5 +``` + +通常将: + +```text +demo/ +``` + +用于课程演示代码, + +```text +exercises/ +``` + +用于自己完成的课后练习。 + +这样可以把“看懂 Demo”和“自己写代码”区分开。 + +--- + +# 课程进度 + +## 第 1 课:Spark 是什么,以及第一个 Spark 程序 + +目录: + +```text +src/main/scala/org/exercises/exercises1/ +``` + +主要学习: + +- Spark 的基本概念 +- `SparkConf` +- `SparkContext` +- 本地模式 `local` +- 从集合创建 RDD +- 从文件创建 RDD +- Transformation +- Action +- 第一个 WordCount + +这一课的目标是先建立 Spark 程序的整体结构。 + +--- + +## 第 2 课:RDD 常用算子 + +目录: + +```text +src/main/scala/org/exercises/exercises2/ +``` + +重点学习单值 RDD 的常见操作: + +| 练习 | 内容 | +| --- | --- | +| Ex 1 | `map` | +| Ex 2 | `filter` | +| Ex 3 | `flatMap` | +| Ex 4 | `reduce` | +| Ex 5 | `foreach` | + +通过这些练习理解: + +```text +RDD + ↓ +Transformation + ↓ +Action +``` + +并逐渐熟悉 Scala 函数、匿名函数以及 Spark 算子的使用方式。 + +--- + +## 第 3 课:PairRDD 与 reduceByKey + +目录: + +```text +src/main/scala/org/exercises/exercises3/ +``` + +重点学习: + +- `(K, V)` 类型的 PairRDD +- `map` +- `reduceByKey` +- WordCount + +核心代码结构: + +```scala +rdd + .flatMap(...) + .map(...) + .reduceByKey(...) + .collect() +``` + +这一课开始正式进入 Spark 中非常重要的 **Key-Value 数据处理模式**。 + +--- + +## 第 4 课:groupByKey、reduceByKey 与 Shuffle + +目录: + +```text +src/main/scala/org/exercises/exercises4/ +``` + +重点学习: + +- `groupByKey` +- `reduceByKey` +- Shuffle +- Map 端局部聚合 +- 宽依赖与窄依赖 +- 为什么 `reduceByKey` 通常比直接 `groupByKey` 更适合做聚合计算 + +通过实际 Demo 对比: + +```text +groupByKey +``` + +和: + +```text +reduceByKey +``` + +的数据处理过程,理解 Shuffle 在 Spark 中到底发生了什么。 + +--- + +## 第 5 课:Partition 分区 + +目录: + +```text +src/main/scala/org/exercises/exercises5/ +``` + +主要学习 Spark RDD 的分区机制。 + +重点内容: + +- `getNumPartitions` +- `glom` +- RDD 分区查看 +- `repartition` +- `coalesce` +- `partitionBy` +- 自定义 `Partitioner` +- Key 到 Partition 的映射 + +例如自定义分区器: + +```scala +class MyPartitioner extends Partitioner { + + override def numPartitions: Int = 2 + + override def getPartition(key: Any): Int = { + key.asInstanceOf[Int] % 2 + } +} +``` + +通过代码理解: + +```text +RDD + ↓ +Partition + ↓ +Task +``` + +以及 Key 如何决定数据最终进入哪个 Partition。 + +当前仓库 `main` 分支最新提交已经进入这一课。 + +--- + +# 线下课程 Demo + +除了 ChatGPT 分课练习之外,仓库还保留了一些线下课程代码,用于补充 Spark 实际开发中的知识。 + +## RDD 创建、操作与重分区 + +主要涉及: + +- RDD 创建 +- RDD Transformation +- RDD Action +- `repartition` +- `coalesce` +- Partition + +--- + +## Cache / Persist / Checkpoint + +用于理解 Spark 中的数据持久化机制,包括: + +- `cache()` +- `persist()` +- `checkpoint()` +- Checkpoint 目录 +- RDD 血缘关系 +- 长依赖链优化 + +同时包含 Spark 程序打包以及向 YARN 提交运行的相关学习内容。 + +--- + +## Spark SQL / DataFrame + +目录: + +```text +src/main/scala/org/example/sql/ +``` + +当前已有 DataFrame 创建示例,通过: + +```scala +SparkSession +``` + +创建 Spark 应用,然后: + +```scala +import spark.implicits._ + +val df = data.toDF() + +df.show() +df.printSchema() +``` + +用于学习: + +- `SparkSession` +- DataFrame +- `toDF` +- `show` +- `printSchema` + +当前仓库已经包含对应的 DataFrame Demo。 + +--- + +# WordCount + +项目中多处使用 WordCount 作为学习案例。 + +典型流程: + +```text +文本 + ↓ +flatMap + ↓ +拆分单词 + ↓ +map + ↓ +(word, 1) + ↓ +reduceByKey + ↓ +统计单词数量 +``` + +Scala 示例: + +```scala +val result = textFile + .flatMap(_.split(" ")) + .map(word => (word, 1)) + .reduceByKey(_ + _) +``` + +WordCount 看起来简单,但可以贯穿整个 Spark 学习过程: + +```text +RDD + ↓ +Transformation + ↓ +PairRDD + ↓ +reduceByKey + ↓ +Shuffle + ↓ +Partition +``` + +因此本项目会反复使用 WordCount 来帮助理解 Spark 的底层处理过程。 + +--- + +# Maven 构建 + +## 拉取依赖 ```bash -# 1) 拉依赖 mvn -DskipTests dependency:resolve - -# 2) 编译(Scala + Java 同时) -mvn compile - -# 3) 跑测试(当前 src/test/ 仍为空,等价于空跑) -mvn test - -# 4) 打 fat jar(含 Spark / Hadoop / MySQL 等所有依赖) -mvn package - -# 5) 产出路径 -ls target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar ``` -### 3. 跑一个 Scala 入口 - -两种方式二选一: - -**方式 A:IDE 直跑**。在 IntelliJ IDEA 里直接打开 `pom.xml`,把以下任一对象作为 `main` 入口运行: - -- `org.example.App` — `local[2]` 模式读 `src/main/resources/word.txt`,做一次 `count + filter + flatMap → reduceByKey` 的 WordCount -- `org.exercises.exercises3.exercises.Exercises5` — 纯数据集合版 WordCount(输出 `hello=3, spark=3, scala=1, is=1, powerful=1`) - -**方式 B:命令行跑 fat jar**。 +## 编译 ```bash -java -cp target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar \ - org.example.App +mvn compile ``` -> 默认 `pom.xml` 没配 `exec-maven-plugin`,所以 `mvn exec:java` 用不了,要么走方式 A,要么上 fat jar。 - -## 三套练习索引 - -### `exercises1` — 第 1 课:Spark 是什么,以及第一个 Spark 程序 - -源:`src/main/scala/org/exercises/exercises1/` -主题:`SparkConf` / `SparkContext` 创建、并行化集合、读取外部文件。 -代表题: - -| 编号 | 题目要点 | -| ----- | ------------------------------------------------- | -| Ex 1 | 搭一个 `SparkContext`,`setMaster("local")` | -| Ex 2 | 从本地/集合源构造 RDD | -| Ex 3 | 基础转换算子 | -| Ex 4 | 触发动作算子 | -| Ex 5 | 综合:第一个本地 WordCount | - -### `exercises2` — 第 2 课:RDD 常用算子 - -源:`src/main/scala/org/exercises/exercises2/` -主题:单值 RDD 的转换与动作。 - -| 编号 | 算子 | -| ----- | ------------ | -| Ex 1 | `map` | -| Ex 2 | `filter` | -| Ex 3 | `flatMap` | -| Ex 4 | `reduce` | -| Ex 5 | `foreach` | - -### `exercises3` — 第 3 课:PairRDD 与 reduceByKey - -源:`src/main/scala/org/exercises/exercises3/` -主题:键值对 RDD 常用算子与正式 WordCount。 - -| 编号 | 算子 / 内容 | -| ----- | -------------------------------------------------------- | -| Ex 5 | 正式练 WordCount:`flatMap → map → reduceByKey → collect` | - -> `exercises1/2/3/demo/` 目录下还有 5 个对应演示,可在写练习前打开当作参考实现。 - -## MySQL 结果表 - -`src/main/resources/create_table.sql` 里两张表是后续评分分析的目标输出: - -| 表名 | 主用途 | -| -------------------------- | ---------------------------------- | -| `top_ten_avg_ratings` | TopN 平均评分电影(`movie_id` 唯一) | -| `genres_average_rating` | 类型平均评分(`genres` 唯一) | - -依赖侧(`pom.xml`)已经预备好: - -- `mysql-connector-java` 8.0.28 -- `HikariCP` 4.0.3 -- `commons-dbutils` 1.6 - -下一步只要写真实的 Spark SQL / DataFrame → JDBC 写入逻辑即可。使用前请在 MySQL 里先执行 `create_table.sql`。 - -## 提交规范 - -仓库已有 6 条 commit,主题风格统一为**简体中文 + 简短摘要**: +## 测试 +```bash +mvn test ``` + +目前测试目录主要用于预留,随着课程推进逐步补充测试。 + +## 打包 + +```bash +mvn package +``` + +完成后会生成: + +```text +target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar +``` + +项目目前使用 `scala-maven-plugin` 编译 Scala,并使用 `maven-assembly-plugin` 打包包含依赖的 Fat JAR。 + +--- + +# 运行程序 + +## IntelliJ IDEA + +推荐直接在 IntelliJ IDEA 中运行。 + +打开: + +```text +pom.xml +``` + +等待 Maven 加载完成后,可以直接找到带有: + +```scala +def main(args: Array[String]): Unit +``` + +的 Scala `object` 运行。 + +例如: + +```text +org.example.App +``` + +或者: + +```text +org.exercises.exercises5.demo.Demo +``` + +具体运行哪个类,根据当前学习章节选择即可。 + +--- + +## 命令行运行 Fat JAR + +例如: + +```bash +java -cp target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar org.example.App +``` + +项目主要用于本地学习和实验,不要求先搭建真正的 Spark 集群。 + +--- + +# 学习方式 + +本项目建议按照下面的方式学习: + +```text +先看 Demo + ↓ +理解代码 + ↓ +自己完成 Exercises + ↓ +运行并观察结果 + ↓ +修改代码验证结论 + ↓ +记录到 Git +``` + +尤其是学习 Spark 时,不建议只看最终输出。 + +例如学习 Partition 时,可以通过: + +```scala +rdd.getNumPartitions +rdd.glom().collect() +``` + +直接观察不同 Partition 中的数据。 + +学习 Shuffle 时,则重点观察: + +```text +数据在不同 Partition 之间如何重新分布 +``` + +这样比单纯记住 API 更容易真正理解 Spark。 + +--- + +# Git 提交规范 + +当前项目采用简洁的中文 Commit Message。 + +推荐格式: + +```text +feat:ChatGPT 第 X 课:课程主题 +``` + +例如: + +```text feat:ChatGPT 第 1 课:Spark 是什么,以及第一个 Spark 程序 feat:ChatGPT 第 2 课:RDD 常用算子 feat:ChatGPT 第 3 课:PairRDD 与 reduceByKey -线下课:RDD的创建、RDD的操作、RDD重分区 -feat:首次提交 +feat:ChatGPT 第 4 课:groupByKey、reduceByKey 与 Shuffle +feat:ChatGPT 第 5 课:Partition 分区 ``` -继续沿用 `feat:<主题>` 形式,**中文一句话主题**,变更保持原子(新题 / 新课一个 commit)。默认分支 `main`,从 `main` 拉 feature 分支再合,禁止直接 push。 +其他类型: -## 安全约定 +```text +docs:README +build:更新 Maven 依赖 +``` -- `target/`、`.idea/`、`*.iml` 等构建/IDE 产物已在 `.gitignore` -- **MySQL / Hadoop 凭据**一律外部化(环境变量或 `*.local.properties`,并加入 `.gitignore`),不要写进 `src/main/resources/` -- 不要把含个人 token 的 fat jar 提交到仓库 +建议一节课一个主要 Commit,一道独立练习尽量保持原子提交。 -## 下一步 +--- -- [ ] `src/test/` 仍为空,按练习补 JUnit 3 测试用例 -- [ ] 把 Spark SQL/DataFrame 的分析结果真正写回 `top_ten_avg_ratings`、`genres_average_rating` -- [ ] 加一份 `.mvn/wrapper/maven-wrapper.properties` 让团队对齐 Maven 版本 +# 开发约定 + +### Java / Scala + +项目同时保留 Java 和 Scala 两套实现。 + +学习阶段以 Scala 为主,Java Demo 主要用于: + +- 对照 Spark API +- 理解 Java 与 Scala 的写法差异 +- 复习 Spark 基础 API + +新课程练习优先使用 Scala。 + +### Spark 模式 + +学习阶段主要使用: + +```scala +.setMaster("local") +``` + +或者: + +```scala +.setMaster("local[2]") +``` + +部分 DataFrame Demo 使用本地多线程模式。 + +整个项目定位是: + +> **本地 Spark 学习、实验与知识整理仓库** + +而不是生产环境 Spark 项目。 + +--- + +# MySQL / JDBC 扩展 + +项目 Maven 依赖中已经包含: + +- MySQL Connector +- HikariCP +- commons-dbutils + +资源目录中也保留: + +```text +src/main/resources/create_table.sql +``` + +用于后续 Spark 数据处理结果写入 MySQL 的实验。 + +后续可以继续完善: + +```text +Spark + ↓ +RDD / DataFrame + ↓ +数据分析 + ↓ +JDBC + ↓ +MySQL +``` + +逐步从 Spark 基础练习过渡到实际的数据处理流程。 + +--- + +# 当前学习进度 + +```text +✅ 第 1 课:Spark 入门 +✅ 第 2 课:RDD 常用算子 +✅ 第 3 课:PairRDD 与 reduceByKey +✅ 第 4 课:groupByKey、reduceByKey 与 Shuffle +✅ 第 5 课:Partition 分区 + +✅ 线下课:RDD 创建 / 操作 / 重分区 +✅ 线下课:Cache / Persist / Checkpoint +✅ 线下课:Spark 程序打包 / YARN +✅ 线下课:创建 DataFrame + +⬜ 后续课程:继续学习 Spark Core +⬜ 后续课程:Spark SQL / DataFrame 深入 +⬜ 后续课程:真实数据分析案例 +``` + +--- + +# 后续规划 + +后续按照课程继续补充: + +```text +Spark Core + ↓ +RDD + ↓ +Shuffle + ↓ +Partition + ↓ +持久化 + ↓ +广播变量 / 累加器 + ↓ +Spark SQL + ↓ +DataFrame + ↓ +数据分析案例 + ↓ +MySQL / JDBC +``` + +项目会尽量保持: + +- 一课一目录 +- Demo 与 Exercises 分离 +- 代码可以直接运行 +- 中文注释辅助理解 +- 每个知识点配合实际输出验证 +- 用 Git 记录学习进度 + +--- + +## 项目定位 + +这是一个以 **“边学、边写、边运行、边总结”** 为核心的 Spark 学习仓库。 + +相比只保存课堂代码,本项目更关注: + +```text +为什么这么写 +↓ +代码实际做了什么 +↓ +Spark 底层发生了什么 +↓ +运行结果为什么是这样 +``` + +通过持续增加 Demo 和练习,将 Spark 的知识点逐渐串联起来,形成一套完整的个人学习笔记与实践代码库。 \ No newline at end of file