修正点:第8课实际是cache/persist/checkpoint(README误写为flatMap);第9课尚未开课但README已写描述;项目结构补充exercises4~8、org.example.sql、App2、hive-site.xml;spark-submit注释里类路径 org.example.core.App2 改回 org.example.App2。新增安全提醒章节记录MySQL凭据明文问题。
331 lines
12 KiB
Markdown
331 lines
12 KiB
Markdown
# Spark 学习项目
|
||
|
||
> 仓库:[`KennethCheng/spark_20260915`](https://gitea.kennethcheng.cc/KennethCheng/spark_20260915)
|
||
|
||
我的 **Scala + Java 双实现 Spark 学习项目**,统一放在一个 Maven 工程里。代码分两条线:
|
||
|
||
- `chapter` —— 线下课程 Demo(`org.example.*`):核心算子 / 分区 / 缓存 / Checkpoint / Spark SQL / Spark On Hive
|
||
- `practise` —— ChatGPT 课程练习(`org.exercises.exercisesN.*`):每课一目录,含 `demo/` 演示 + `exercises/` 练习题
|
||
|
||
主线按 **Spark 2.4.3 / Scala 2.11 / JDK 8** 学习,从 RDD 入门到 Spark SQL / Hive / JDBC / Yarn 提交。
|
||
|
||
---
|
||
|
||
## ⚠️ 安全提醒(先看)
|
||
|
||
- `src/main/resources/hive-site.xml` 与 `src/main/scala/org/example/sql/*.scala` 中含有**明文 MySQL 账号密码**与内网 JDBC URL(参见审查报告)
|
||
- 当前仓库是公开的 Gitea,凭据已暴露在 git 历史中,**先轮换密码再继续**
|
||
- AGENTS.md 已要求凭据外部化(环境变量 / `*.local.properties` + `.gitignore`),本次重写 README 不修代码,仅做记录
|
||
|
||
---
|
||
|
||
## 技术栈
|
||
|
||
| 类别 | 版本 |
|
||
| --- | --- |
|
||
| JDK | 8 |
|
||
| Scala | 2.11.12 |
|
||
| Apache Spark | 2.4.3(core / sql / hive) |
|
||
| Hadoop Client | 2.7.3 |
|
||
| Maven | Maven 3 |
|
||
| scala-maven-plugin | 4.8.1 |
|
||
| maven-assembly-plugin | 3.7.1 |
|
||
| MySQL Connector | 8.0.28 |
|
||
| HikariCP | 4.0.3 |
|
||
| commons-dbutils | 1.6 |
|
||
| JUnit | 3.8.1(test scope,当前无用例) |
|
||
|
||
---
|
||
|
||
## 项目结构
|
||
|
||
```text
|
||
spark_project/
|
||
├── pom.xml
|
||
├── AGENTS.md # Agent / 协作规约(必读)
|
||
├── README.md
|
||
│
|
||
├── src/main/java/org/example/ # Java 版核心 Demo
|
||
│ ├── AppTest.java
|
||
│ └── core/
|
||
│ ├── GroupByKeyDemo.java
|
||
│ ├── MyPartitioner.java
|
||
│ ├── RddCreateDemo.java
|
||
│ └── RepartitionVsCoaleasce.java
|
||
│
|
||
├── src/main/scala/org/example/ # Scala 版核心 Demo(与 Java 同名 4 个 + 扩展)
|
||
│ ├── App.scala # local[2] 入口:word.txt WordCount
|
||
│ ├── App2.scala # spark-submit 集群提交示例(注释里有完整命令)
|
||
│ ├── test.scala
|
||
│ ├── core/
|
||
│ │ ├── GroupByKeyDemo.scala
|
||
│ │ ├── MyPartitioner.scala
|
||
│ │ ├── RddCreateDemo.scala
|
||
│ │ ├── RepartitionVsCoalesce.scala
|
||
│ │ ├── CacheTest.scala
|
||
│ │ ├── DemoCache.scala # cache + persist 性能对比
|
||
│ │ ├── DemoCacheNoCache.scala
|
||
│ │ ├── CheckpointDemo.scala # setCheckpointDir + checkpoint()
|
||
└── ReadFromCheckpoint.scala
|
||
│ └── sql/ # Spark SQL 线下课 Demo
|
||
│ ├── CreateDataFrame.scala # RDD+Schema / CSV / JDBC 三种方式
|
||
│ ├── DataFrameDSL.scala # select / col / as
|
||
│ ├── DataFrameSql.scala # createTempView + spark.sql
|
||
│ └── SparkOnHive.scala # enableHiveSupport + spark.sql
|
||
│
|
||
├── src/main/scala/org/exercises/ # ChatGPT 课程练习(按课号递增)
|
||
│ ├── exercises1/ # 第 1 课:Spark 入门
|
||
│ │ ├── demo/ (DemoS1tart / Demo2CreateRDD / Demo3RddMap)
|
||
│ │ └── exercises/ (Exercises1~5)
|
||
│ ├── exercises2/ # 第 2 课:RDD 算子 map / filter / flatMap / reduce / foreach
|
||
│ ├── exercises3/ # 第 3 课:PairRDD / reduceByKey / WordCount
|
||
│ ├── exercises4/ # 第 4 课:groupByKey vs reduceByKey / Shuffle
|
||
│ ├── exercises5/ # 第 5 课:Partition / partitionBy / 自定义 Partitioner
|
||
│ ├── exercises6/ # 第 6 课:窄依赖 / 宽依赖 / Stage / Task
|
||
│ ├── exercises7/ # 第 7 课:cache / persist / mapPartitions
|
||
│ └── exercises8/ # 第 8 课:cache / persist / checkpoint
|
||
│
|
||
└── src/main/resources/
|
||
├── create_table.sql # 离线分析结果目标表(top_ten_avg_ratings / genres_average_rating)
|
||
├── hive-site.xml # ⚠️ 含明文凭据,待外部化
|
||
├── log4j.properties
|
||
├── word.txt # WordCount 示例输入
|
||
└── spark/sql/student.csv # DataFrame 读取示例
|
||
```
|
||
|
||
> `src/main/resources/tmp/spark-checkpoint/` 是运行时产物,已被运行时写入但**不应进仓库**,后续会加入 `.gitignore`。
|
||
|
||
---
|
||
|
||
## ChatGPT 课程进度
|
||
|
||
| 课号 | 主题 | 目录 | 演示 Demo | 状态 |
|
||
| --- | --- | --- | --- | --- |
|
||
| 1 | Spark 入门(Conf / Context / parallelize / textFile / Transformation vs Action) | `exercises1` | `DemoS1tart`、`Demo2CreateRDD`、`Demo3RddMap` | ✅ |
|
||
| 2 | RDD 算子:map / filter / flatMap / reduce / foreach | `exercises2` | `Demo1Map` ~ `Demo5Foreach` | ✅ |
|
||
| 3 | PairRDD / mapValues / reduceByKey / groupByKey / WordCount | `exercises3` | `Demo1Map` ~ `Demo5WordCount` | ✅ |
|
||
| 4 | groupByKey vs reduceByKey / Shuffle | `exercises4` | `Demo1GroupByKeyAndReduceByKey`、`Demo2GroupByKeyVsReduceByKey` | ✅ |
|
||
| 5 | Partition / `partitionBy` / 自定义 `Partitioner` / repartition / coalesce | `exercises5` | `Demo1PartitionBasic` ~ `Demo4MyPartitioner` | ✅ |
|
||
| 6 | 窄依赖 / 宽依赖 / Stage / Task / DAG | `exercises6` | `Demo.scala` | ✅ |
|
||
| 7 | Cache / Persist / `mapPartitions` / 性能对比 | `exercises7` | `Demo1CacheAndPersist`、`Demo2MapPartitions` | ✅ |
|
||
| 8 | cache / persist / `setCheckpointDir` / checkpoint 复用 | `exercises8` | `Demo1Checkpoint` | ✅ |
|
||
|
||
每课 5 道练习,统一以 `Exercises1.scala` ~ `Exercises5.scala` 命名,可独立 `main` 运行。
|
||
|
||
---
|
||
|
||
## 线下课 Demo 一览(`org.example.*`)
|
||
|
||
| 包 | Demo | 说明 |
|
||
| --- | --- | --- |
|
||
| `org.example` | `App` | `local[2]` 跑 `word.txt` 的 WordCount 入门入口 |
|
||
| `org.example` | `App2` | 集群 `spark-submit` 模板,注释里写好了完整提交命令 |
|
||
| `org.example.core` | `RddCreateDemo` | `parallelize` + `textFile` 两种 RDD 创建方式 |
|
||
| `org.example.core` | `GroupByKeyDemo` | `groupByKey` 行为与输出观察 |
|
||
| `org.example.core` | `RepartitionVsCoalesce` | `repartition` vs `coalesce` 区别 |
|
||
| `org.example.core` | `MyPartitioner` | 自定义 `Partitioner` |
|
||
| `org.example.core` | `DemoCache` / `DemoCacheNoCache` / `CacheTest` | `cache` / `persist(MEMORY_ONLY)` 是否真的更快 |
|
||
| `org.example.core` | `CheckpointDemo` / `ReadFromCheckpoint` | `setCheckpointDir` + 复用 checkpoint 切断 lineage |
|
||
| `org.example.sql` | `CreateDataFrame` | RDD+Schema / CSV / JDBC 三种建 DF 方式 |
|
||
| `org.example.sql` | `DataFrameDSL` | DSL:`select` / `col` / `as` |
|
||
| `org.example.sql` | `DataFrameSql` | `createTempView` + `spark.sql` |
|
||
| `org.example.sql` | `SparkOnHive` | `enableHiveSupport()` + `spark.sql("use gmall")` |
|
||
| `org.example` | `test.scala` | 随手调试用,可忽略 |
|
||
|
||
> `org.example.core.*` 在 `src/main/java` 下还有 4 个 Java 版同名 Demo,方便对照两种语言写法。
|
||
|
||
---
|
||
|
||
## 当前已掌握的 RDD 知识体系
|
||
|
||
```text
|
||
Spark
|
||
├── SparkConf / SparkContext / SparkSession
|
||
│
|
||
├── RDD
|
||
│ ├── 创建
|
||
│ │ ├── parallelize(makeRDD)
|
||
│ │ └── textFile
|
||
│ │
|
||
│ ├── Transformation(Lazy)
|
||
│ │ ├── map / mapPartitions
|
||
│ │ ├── filter
|
||
│ │ ├── flatMap
|
||
│ │ ├── groupBy / sortBy / sortWith
|
||
│ │ └── reduceByKey / groupByKey
|
||
│ │
|
||
│ ├── Action(触发 Job)
|
||
│ │ ├── collect / count / take
|
||
│ │ ├── reduce / foreach
|
||
│ │ └── saveAsTextFile
|
||
│ │
|
||
│ ├── Partition
|
||
│ │ ├── getNumPartitions / glom
|
||
│ │ ├── partitionBy(自定义 Partitioner)
|
||
│ │ ├── repartition ← 触发 Shuffle
|
||
│ │ └── coalesce ← 默认不 Shuffle
|
||
│ │
|
||
│ └── 性能
|
||
│ ├── Lazy Evaluation
|
||
│ ├── cache / persist(StorageLevel)
|
||
│ └── checkpoint(切断 lineage,依赖可靠文件系统)
|
||
│
|
||
└── PairRDD
|
||
├── reduceByKey / foldByKey
|
||
├── groupByKey
|
||
└── 自定义 Partitioner
|
||
```
|
||
|
||
---
|
||
|
||
## Spark SQL / DataFrame / Hive
|
||
|
||
RDD 基础阶段完成后,开始 Spark SQL:
|
||
|
||
- `SparkSession.builder().enableHiveSupport().getOrCreate()`
|
||
- DataFrame 创建:RDD+Schema / `spark.read.csv(...)` / `spark.read.format("jdbc")`
|
||
- DSL:`select` / `col` / `as`
|
||
- SQL:`createTempView` + `spark.sql(...)`
|
||
- Hive:`spark.sql("use gmall")` + 聚合查询
|
||
- 目标:DataFrame 聚合结果 → JDBC → MySQL(建表 SQL 在 `create_table.sql`)
|
||
|
||
---
|
||
|
||
## Spark 集群实践
|
||
|
||
| 项目 | 说明 |
|
||
| --- | --- |
|
||
| `spark-submit` | `App2.scala` 注释里给出完整 yarn-client 提交命令 |
|
||
| Spark UI | 本地 `local[2]` 跑起来后 `http://localhost:4040`(Jobs / Stages / Storage / SQL) |
|
||
| History Server | `18080`(仅历史任务) |
|
||
| Yarn Client | `--master yarn --deploy-mode client` |
|
||
| HDFS | `textFile("hdfs://...")` 见 `RddCreateDemo` 注释 |
|
||
| Hive / Beeline / DBeaver | 已跑通 `enableHiveSupport` 链路 |
|
||
|
||
注意:
|
||
|
||
- `4040` 是当前 Application 的实时 UI,结束即失效
|
||
- `18080` 是 History Server,两者不是一回事
|
||
|
||
---
|
||
|
||
## 快速开始
|
||
|
||
### 1. 环境
|
||
|
||
JDK 8、Scala 2.11.x、Maven 3.x,本地能 `mvn` 即可。
|
||
|
||
### 2. 拉依赖与编译
|
||
|
||
```bash
|
||
mvn -DskipTests dependency:resolve
|
||
mvn compile
|
||
```
|
||
|
||
### 3. 打包(可选)
|
||
|
||
```bash
|
||
mvn package
|
||
```
|
||
|
||
产物:
|
||
|
||
```text
|
||
target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar
|
||
```
|
||
|
||
### 4. 跑单个 Demo
|
||
|
||
任选其一:
|
||
|
||
- IDE 里把目标 `object ExercisesX` / `object Xxx` / `class Xxx` 当作 `main` 直接 Run
|
||
- 命令行:
|
||
|
||
```bash
|
||
java -cp target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar org.example.App
|
||
```
|
||
|
||
集群提交参考 `App2.scala` 顶部注释。
|
||
|
||
---
|
||
|
||
## MySQL / Hive 配置
|
||
|
||
- JDBC 表结构:`src/main/resources/create_table.sql`
|
||
- `top_ten_avg_ratings` —— Top10 平均评分电影
|
||
- `genres_average_rating` —— 类型平均评分
|
||
- Hive:`src/main/resources/hive-site.xml`(⚠️ 内含明文凭据,待外部化)
|
||
- 依赖:`mysql-connector-java` / `HikariCP` / `commons-dbutils`
|
||
|
||
凭据外部化建议(参考 AGENTS.md):
|
||
|
||
```text
|
||
src/main/resources/
|
||
├── application.properties.example # 模板:仅占位符,提交到仓库
|
||
└── application.local.properties # 真凭据,gitignore
|
||
|
||
读取:
|
||
val props = new Properties()
|
||
props.load(getClass.getResourceAsStream("/application.properties"))
|
||
props.load(getClass.getResourceAsStream("/application.local.properties"))
|
||
```
|
||
|
||
---
|
||
|
||
## 代码规范与提交流程
|
||
|
||
- 单文件尽量一个知识点,命名沿用 `DemoXxx` / `ExercisesX` / `XxxDemo`
|
||
- 中文注释 + **中文 commit message**
|
||
- 推荐样式:
|
||
|
||
```text
|
||
feat:线下课:集合_xxx
|
||
feat:ChatGPT 第 7 课:缓存 Cache / Persist + mapPartitions
|
||
fix:修复 ...
|
||
docs:更新 README
|
||
refactor:重构 ...
|
||
```
|
||
|
||
- 单课 / 单练习保持原子提交
|
||
- `main` 分支受保护,从 `main` 拉 feature 分支再 PR(详见 AGENTS.md)
|
||
|
||
---
|
||
|
||
## 学习路线
|
||
|
||
```text
|
||
Scala 基础
|
||
↓
|
||
RDD 入门(parallelize / textFile / Transformation vs Action)
|
||
↓
|
||
RDD 算子(map / filter / flatMap / reduce / foreach)
|
||
↓
|
||
PairRDD(reduceByKey / groupByKey / WordCount)
|
||
↓
|
||
Partition / Shuffle / 自定义 Partitioner
|
||
↓
|
||
依赖 / DAG / Stage / Task
|
||
↓
|
||
Cache / Persist / Checkpoint
|
||
↓
|
||
Spark SQL(DataFrame / DSL / SQL)
|
||
↓
|
||
Hive(enableHiveSupport / Metastore)
|
||
↓
|
||
JDBC 读写 MySQL
|
||
↓
|
||
Spark Thrift Server / Beeline / DBeaver
|
||
↓
|
||
Yarn / spark-submit / 集群运行
|
||
↓
|
||
完整数据分析项目
|
||
```
|
||
|
||
---
|
||
|
||
## 相关资源
|
||
|
||
- Apache Spark:<https://spark.apache.org/>
|
||
- Apache Hadoop:<https://hadoop.apache.org/>
|
||
- Apache Maven:<https://maven.apache.org/>
|
||
- Hive 配置文档:<https://cwiki.apache.org/confluence/display/Hive/Configuration+Properties> |