612 lines
9.9 KiB
Markdown
612 lines
9.9 KiB
Markdown
# Spark 学习项目
|
||
|
||
> 仓库:[`KennethCheng/spark_20260915`](https://gitea.kennethcheng.cc/KennethCheng/spark_20260915)
|
||
|
||
这是我的 **Scala + Spark 学习与练习项目**,主要用于记录跟随课程学习 Spark 的过程,并把课堂 Demo、课后练习和实际运行实验整理到同一个 Maven 项目中。
|
||
|
||
当前学习重点以 **Spark Core / RDD** 为主,并逐步过渡到 **Spark SQL / DataFrame / Hive / JDBC**。
|
||
|
||
---
|
||
|
||
## 学习方式
|
||
|
||
项目中的代码主要分成两类:
|
||
|
||
- `chapter`:线下课程内容、课堂 Demo、知识点验证
|
||
- `practise`:配合 ChatGPT 学习完成的课后练习
|
||
|
||
代码统一放在 Maven 的 `src/main/scala` / `src/main/java` 下,尽量保持一个知识点一个 Demo,方便单独运行和复习。
|
||
|
||
提交信息统一使用中文,例如:
|
||
|
||
```text
|
||
feat:线下课:集合_...
|
||
feat:ChatGPT第 7 课:缓存 Cache / Persist + mapPartitions
|
||
```
|
||
|
||
---
|
||
|
||
## 技术栈
|
||
|
||
| 类别 | 版本 |
|
||
| --- | --- |
|
||
| JDK | Java 8 |
|
||
| Scala | 2.11.12 |
|
||
| Apache Spark | 2.4.3 |
|
||
| 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 |
|
||
|
||
> 项目以 **Spark 2.4.3 + Scala 2.11 + JDK 8** 为基准,运行环境不要随意升级 JVM / Scala 版本。
|
||
|
||
---
|
||
|
||
## 项目结构
|
||
|
||
```text
|
||
spark_project/
|
||
├── pom.xml
|
||
├── AGENTS.md
|
||
├── README.md
|
||
│
|
||
├── src/main/java/org/example/
|
||
│ ├── core/
|
||
│ │ ├── GroupByKeyDemo.java
|
||
│ │ ├── MyPartitioner.java
|
||
│ │ ├── RddCreateDemo.java
|
||
│ │ └── RepartitionVsCoalesce.java
|
||
│ └── AppTest.java
|
||
│
|
||
├── src/main/scala/org/example/
|
||
│ ├── App.scala
|
||
│ ├── test.scala
|
||
│ └── core/
|
||
│ ├── GroupByKeyDemo.scala
|
||
│ ├── MyPartitioner.scala
|
||
│ ├── RddCreateDemo.scala
|
||
│ └── RepartitionVsCoalesce.scala
|
||
│
|
||
├── src/main/scala/org/exercises/
|
||
│ ├── exercises1/
|
||
│ ├── exercises2/
|
||
│ ├── exercises3/
|
||
│ └── ...
|
||
│
|
||
└── src/main/resources/
|
||
├── create_table.sql
|
||
├── log4j.properties
|
||
└── word.txt
|
||
```
|
||
|
||
---
|
||
|
||
## Spark 学习进度
|
||
|
||
### 第 1 课:Spark 入门
|
||
|
||
主要内容:
|
||
|
||
- `SparkConf`
|
||
- `SparkContext`
|
||
- `parallelize`
|
||
- 从文件创建 RDD
|
||
- Spark 程序基本运行方式
|
||
- Transformation / Action 基本概念
|
||
|
||
对应练习:
|
||
|
||
```text
|
||
src/main/scala/org/exercises/exercises1/
|
||
```
|
||
|
||
---
|
||
|
||
### 第 2 课:RDD 常用算子
|
||
|
||
主要学习:
|
||
|
||
- `map`
|
||
- `filter`
|
||
- `flatMap`
|
||
- `reduce`
|
||
- `foreach`
|
||
|
||
重点理解:
|
||
|
||
```text
|
||
Transformation
|
||
↓
|
||
生成新的 RDD
|
||
↓
|
||
继续构建计算链
|
||
|
||
Action
|
||
↓
|
||
触发 Job 执行
|
||
↓
|
||
返回结果 / 输出结果
|
||
```
|
||
|
||
对应练习:
|
||
|
||
```text
|
||
src/main/scala/org/exercises/exercises2/
|
||
```
|
||
|
||
---
|
||
|
||
### 第 3 课:PairRDD / WordCount
|
||
|
||
主要学习:
|
||
|
||
- `(K, V)` 键值对 RDD
|
||
- `map` 生成 PairRDD
|
||
- `reduceByKey`
|
||
- WordCount
|
||
- `groupByKey`
|
||
- shuffle 的基本概念
|
||
|
||
典型流程:
|
||
|
||
```text
|
||
文本
|
||
↓
|
||
flatMap
|
||
↓
|
||
map
|
||
↓
|
||
reduceByKey
|
||
↓
|
||
collect
|
||
```
|
||
|
||
对应练习:
|
||
|
||
```text
|
||
src/main/scala/org/exercises/exercises3/
|
||
```
|
||
|
||
---
|
||
|
||
### 第 4~5 课:Scala 函数基础 + Spark 分区
|
||
|
||
Scala 部分重点:
|
||
|
||
- `def`
|
||
- `=>`
|
||
- `map`
|
||
- 函数作为参数
|
||
- `_` 下划线简写
|
||
- Lambda / 函数值
|
||
- 函数参数与返回值
|
||
|
||
Spark 部分重点:
|
||
|
||
- Partition
|
||
- `getNumPartitions`
|
||
- `glom`
|
||
- `mapPartitions`
|
||
- `partitionBy`
|
||
- 自定义 `Partitioner`
|
||
- `groupByKey`
|
||
- `reduceByKey`
|
||
- `repartition`
|
||
- `coalesce`
|
||
|
||
项目中的核心 Demo:
|
||
|
||
```text
|
||
src/main/scala/org/example/core/
|
||
src/main/java/org/example/core/
|
||
```
|
||
|
||
---
|
||
|
||
### 第 6~7 课:RDD 执行过程与缓存
|
||
|
||
主要内容:
|
||
|
||
- Transformation 与 Action
|
||
- Lazy Evaluation
|
||
- DAG
|
||
- Job / Stage / Task
|
||
- Cache
|
||
- Persist
|
||
- `mapPartitions`
|
||
- 多次 Action 时为什么缓存可能提升性能
|
||
- Spark UI 中查看 Job / Stage / Task
|
||
|
||
重点认识:
|
||
|
||
```text
|
||
Transformation
|
||
↓
|
||
只记录计算逻辑
|
||
↓
|
||
Action
|
||
↓
|
||
真正触发 Job
|
||
```
|
||
|
||
以及:
|
||
|
||
```text
|
||
RDD
|
||
├── cache()
|
||
└── persist(...)
|
||
```
|
||
|
||
缓存不是“加了就一定更快”,只有同一个 RDD 被重复使用、重新计算成本较高时,收益才比较明显。
|
||
|
||
---
|
||
|
||
### 第 8 课:flatMap 与嵌套集合
|
||
|
||
主要学习:
|
||
|
||
- `map` 与 `flatMap` 的区别
|
||
- 嵌套集合处理
|
||
- 一对多转换
|
||
- WordCount 中为什么常用 `flatMap`
|
||
- 不使用 `flatMap` 的替代实现
|
||
|
||
典型理解:
|
||
|
||
```text
|
||
map
|
||
1 -> List(1, 2)
|
||
2 -> List(3, 4)
|
||
|
||
结果:
|
||
List(List(1, 2), List(3, 4))
|
||
```
|
||
|
||
```text
|
||
flatMap
|
||
1 -> List(1, 2)
|
||
2 -> List(3, 4)
|
||
|
||
结果:
|
||
List(1, 2, 3, 4)
|
||
```
|
||
|
||
---
|
||
|
||
### 第 9 课:排序与分组
|
||
|
||
主要学习:
|
||
|
||
- `sortBy`
|
||
- `sortWith`
|
||
- `groupBy`
|
||
- 分组后 `Iterable / CompactBuffer` 的理解
|
||
- 排序与分组的使用场景
|
||
- PairRDD 与普通 RDD 的进一步操作
|
||
|
||
常见写法:
|
||
|
||
```scala
|
||
rdd.sortBy(_._2)
|
||
rdd.sortWith((a, b) => a._2 > b._2)
|
||
rdd.groupBy(_ % 2)
|
||
```
|
||
|
||
---
|
||
|
||
## 当前已掌握的 RDD 知识体系
|
||
|
||
```text
|
||
Spark
|
||
│
|
||
├── SparkConf / SparkContext
|
||
│
|
||
├── RDD
|
||
│ ├── 创建
|
||
│ │ ├── parallelize
|
||
│ │ └── textFile
|
||
│ │
|
||
│ ├── Transformation
|
||
│ │ ├── map
|
||
│ │ ├── filter
|
||
│ │ ├── flatMap
|
||
│ │ ├── mapPartitions
|
||
│ │ ├── groupBy
|
||
│ │ ├── sortBy
|
||
│ │ ├── sortWith
|
||
│ │ └── reduceByKey
|
||
│ │
|
||
│ ├── Action
|
||
│ │ ├── collect
|
||
│ │ ├── count
|
||
│ │ ├── reduce
|
||
│ │ └── foreach
|
||
│ │
|
||
│ ├── Partition
|
||
│ │ ├── glom
|
||
│ │ ├── getNumPartitions
|
||
│ │ ├── partitionBy
|
||
│ │ ├── repartition
|
||
│ │ └── coalesce
|
||
│ │
|
||
│ └── 性能
|
||
│ ├── cache
|
||
│ ├── persist
|
||
│ └── Lazy Evaluation
|
||
│
|
||
└── PairRDD
|
||
├── reduceByKey
|
||
├── groupByKey
|
||
└── 自定义 Partitioner
|
||
```
|
||
|
||
---
|
||
|
||
## Spark SQL / DataFrame / Hive
|
||
|
||
RDD 基础阶段完成后,项目继续进入 Spark SQL 相关内容:
|
||
|
||
- `SparkSession`
|
||
- DataFrame
|
||
- DataSet 基础概念
|
||
- Schema
|
||
- DSL 查询
|
||
- SQL 查询
|
||
- JDBC
|
||
- Hive 支持
|
||
- Spark Thrift Server
|
||
- DBeaver 连接 Spark SQL
|
||
- DataFrame 与 JDBC 数据写入
|
||
|
||
目前已经开始接触:
|
||
|
||
```scala
|
||
val spark = SparkSession.builder()
|
||
.appName("Spark On Hive")
|
||
.enableHiveSupport()
|
||
.getOrCreate()
|
||
```
|
||
|
||
以及:
|
||
|
||
```scala
|
||
spark.sql("show databases")
|
||
spark.sql("show tables")
|
||
spark.sql("select ...")
|
||
```
|
||
|
||
后续会把 RDD 学到的思想逐步对应到 DataFrame / SQL 中。
|
||
|
||
---
|
||
|
||
## Spark 集群实践
|
||
|
||
除了本地 `local` 模式,还会进行 Spark 集群环境实验。
|
||
|
||
已经实践过的内容包括:
|
||
|
||
- `spark-submit`
|
||
- Yarn Client
|
||
- HDFS
|
||
- Spark UI
|
||
- Spark History Server
|
||
- Hive / Hive Metastore
|
||
- Spark Thrift Server
|
||
- Beeline
|
||
- DBeaver
|
||
- Spark SQL
|
||
|
||
例如:
|
||
|
||
```bash
|
||
spark-submit \
|
||
--master yarn \
|
||
--deploy-mode client \
|
||
--class org.example.core.App2 \
|
||
spark_project-1.0-SNAPSHOT.jar
|
||
```
|
||
|
||
---
|
||
|
||
## 快速开始
|
||
|
||
### 1. 准备环境
|
||
|
||
确保本机具备:
|
||
|
||
```text
|
||
JDK 8
|
||
Scala 2.11.x
|
||
Maven 3.x
|
||
```
|
||
|
||
### 2. 编译
|
||
|
||
```bash
|
||
mvn -DskipTests dependency:resolve
|
||
|
||
mvn compile
|
||
```
|
||
|
||
### 3. 打包
|
||
|
||
```bash
|
||
mvn package
|
||
```
|
||
|
||
生成:
|
||
|
||
```text
|
||
target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar
|
||
```
|
||
|
||
### 4. IDEA 运行
|
||
|
||
在 IntelliJ IDEA 中直接运行 Scala / Java 中的 `object` 或 `main` 方法即可。
|
||
|
||
---
|
||
|
||
## 本地 Spark UI
|
||
|
||
本地运行 Spark 程序时,可以通过 Spark Web UI 查看:
|
||
|
||
```text
|
||
http://localhost:4040
|
||
```
|
||
|
||
常见页面:
|
||
|
||
```text
|
||
Jobs
|
||
Stages
|
||
Storage
|
||
Environment
|
||
Executors
|
||
SQL
|
||
```
|
||
|
||
注意:
|
||
|
||
- `4040` 是当前 Spark Application 的 Web UI 端口
|
||
- Application 结束后,4040 通常就不再提供该 Application 的实时页面
|
||
- `18080` 是 Spark History Server,不是本地运行 Application 的实时 UI
|
||
|
||
---
|
||
|
||
## MySQL / JDBC
|
||
|
||
`src/main/resources/create_table.sql` 中保留了后续分析结果的目标表。
|
||
|
||
目前主要规划:
|
||
|
||
```text
|
||
Spark DataFrame
|
||
↓
|
||
聚合分析
|
||
↓
|
||
JDBC 写入
|
||
↓
|
||
MySQL
|
||
```
|
||
|
||
目标表:
|
||
|
||
| 表名 | 用途 |
|
||
| --- | --- |
|
||
| `top_ten_avg_ratings` | Top10 平均评分电影 |
|
||
| `genres_average_rating` | 类型平均评分 |
|
||
|
||
相关依赖:
|
||
|
||
```text
|
||
mysql-connector-java
|
||
HikariCP
|
||
commons-dbutils
|
||
```
|
||
|
||
---
|
||
|
||
## 代码规范
|
||
|
||
### Demo 命名
|
||
|
||
按课程 / 知识点拆分 Demo,尽量保持:
|
||
|
||
```text
|
||
Demo1xxx
|
||
Demo2xxx
|
||
...
|
||
```
|
||
|
||
### Commit
|
||
|
||
推荐:
|
||
|
||
```text
|
||
feat:线下课:集合_...
|
||
feat:ChatGPT第 7 课:缓存 Cache / Persist + mapPartitions
|
||
fix:修复 ...
|
||
docs:更新 README
|
||
refactor:重构 ...
|
||
```
|
||
|
||
每个知识点尽量保持一个独立 commit,便于后续按课程回看。
|
||
|
||
---
|
||
|
||
## 学习目标
|
||
|
||
最终形成一套完整的 Spark 学习路线:
|
||
|
||
```text
|
||
Scala 基础
|
||
↓
|
||
RDD 基础
|
||
↓
|
||
PairRDD
|
||
↓
|
||
Partition / Shuffle
|
||
↓
|
||
Cache / DAG / Stage / Task
|
||
↓
|
||
Spark SQL / DataFrame
|
||
↓
|
||
Hive
|
||
↓
|
||
JDBC
|
||
↓
|
||
Spark Thrift Server
|
||
↓
|
||
Yarn / 集群运行
|
||
↓
|
||
完整数据分析项目
|
||
```
|
||
|
||
重点不是只记 API,而是逐步理解:
|
||
|
||
```text
|
||
代码怎么写
|
||
↓
|
||
RDD 怎么产生
|
||
↓
|
||
Transformation 怎么串起来
|
||
↓
|
||
Action 什么时候触发
|
||
↓
|
||
Spark 如何拆成 Job / Stage / Task
|
||
↓
|
||
数据如何在 Partition 之间流动
|
||
↓
|
||
Shuffle 为什么发生
|
||
↓
|
||
为什么某些写法性能更好
|
||
```
|
||
|
||
---
|
||
|
||
## 学习记录
|
||
|
||
| 阶段 | 内容 | 状态 |
|
||
| --- | --- | --- |
|
||
| 01 | Spark 入门 | ✅ |
|
||
| 02 | RDD 常用算子 | ✅ |
|
||
| 03 | PairRDD / WordCount | ✅ |
|
||
| 04 | Scala 函数基础 | ✅ |
|
||
| 05 | 函数作为参数 / Partition | ✅ |
|
||
| 06 | RDD 执行与惰性计算 | ✅ |
|
||
| 07 | Cache / Persist / mapPartitions | ✅ |
|
||
| 08 | flatMap / 嵌套集合 | ✅ |
|
||
| 09 | sortBy / sortWith / groupBy | ✅ |
|
||
| 10+ | Spark SQL / DataFrame / Hive | 🚧 |
|
||
|
||
---
|
||
|
||
## 相关资源
|
||
|
||
- Apache Spark:<https://spark.apache.org/>
|
||
- Apache Hadoop:<https://hadoop.apache.org/>
|
||
- Apache Maven:<https://maven.apache.org/>
|