- README.md:项目结构、技术栈、三条业务需求、MySQL 表、构建运行步骤、已知问题 - CODE_REVIEW.md:分三级(严重/主要/次要)列出 11 个发现,含密码硬编码、空类、Any 分发等 本次仅提交两份文档;工作树里 App.scala 的 data path 切换与 ratings_all.csv 由用户后续单独提交
165 lines
5.0 KiB
Markdown
165 lines
5.0 KiB
Markdown
# spark_project
|
||
|
||
MovieLens 风格数据集上的三条聚合分析练习项目:基于 Spark SQL + MySQL,把三条统计结果写入数据库。
|
||
|
||
> 学习 / 练手项目;不是生产系统。详见 `CODE_REVIEW.md`。
|
||
|
||
---
|
||
|
||
## 项目简介
|
||
|
||
读取两份 CSV:
|
||
|
||
- `data/movies.csv`:电影元数据(`movieId, title, genres`)
|
||
- `data/ratings_all.csv`(或 `data/ratings.csv`,小样本):用户评分(`userId, movieId, rating, timestamp`)
|
||
|
||
完成三条业务需求:
|
||
|
||
| 编号 | 描述 |
|
||
| :--: | :-- |
|
||
| 需求 1 | 评分次数 > 5000 的电影中,按平均分 Top 10,输出 `movieId / title / avgRating` |
|
||
| 需求 2 | 按 `genres` 拆分后,每个类别计算平均评分 |
|
||
| 需求 3 | 按评分次数 Top 10 的电影,输出 `movieId / title / ratingCount` |
|
||
|
||
三条结果分别写入 MySQL 表 `top_ten_avg_ratings` / `genres_average_rating` / `movie_rating_count`,使用 `REPLACE INTO`(基于各自 unique key 做 upsert)。
|
||
|
||
---
|
||
|
||
## 技术栈
|
||
|
||
| 组件 | 版本 |
|
||
| :-- | :-- |
|
||
| Scala | 2.11.12 |
|
||
| Apache Spark | 2.4.3(Spark Core + Spark SQL)|
|
||
| MySQL Connector/J | 8.0.28 |
|
||
| HikariCP | 4.0.3 |
|
||
| Commons DBUtils | 1.6 |
|
||
| 构建工具 | Maven 3.x + `scala-maven-plugin` 4.8.1 |
|
||
| Java 目标 | 8 |
|
||
|
||
---
|
||
|
||
## 目录结构
|
||
|
||
```
|
||
spark_project/
|
||
├── pom.xml
|
||
├── README.md
|
||
├── CODE_REVIEW.md
|
||
├── data/
|
||
│ ├── movies.csv # 电影元数据
|
||
│ ├── ratings.csv # 小样本评分
|
||
│ └── ratings_all.csv # 全量评分
|
||
└── src/
|
||
├── main/
|
||
│ ├── resources/
|
||
│ │ ├── create_table.sql # MySQL DDL
|
||
│ │ └── log4j.properties # 日志配置(stdout only,error 级别)
|
||
│ └── scala/com/example/
|
||
│ ├── spark/
|
||
│ │ ├── App.scala # 入口,三个 SQL 需求
|
||
│ │ └── Entitys.scala # case class:Movie / Rating / 三种结果
|
||
│ └── utils/
|
||
│ └── JDBCUtil.scala # HikariCP + DBUtils 写入 MySQL
|
||
└── test/ # 空
|
||
```
|
||
|
||
---
|
||
|
||
## 数据约定
|
||
|
||
- `data/movies.csv` 字段:`movieId, title, genres`
|
||
- `genres` 多个类别用 `|` 分隔(如 `Adventure|Animation|Children|Comedy|Fantasy`)
|
||
- `data/ratings.csv` / `data/ratings_all.csv` 字段:`userId, movieId, rating, timestamp`
|
||
- 默认入口读 `ratings_all.csv`;开发期可在 `App.scala:9` 切回 `ratings.csv`
|
||
|
||
---
|
||
|
||
## MySQL 表结构
|
||
|
||
详见 `src/main/resources/create_table.sql`。三张表统一字段:`id` 自增主键、`update_time` 默认当前时间、业务字段 + 唯一键。
|
||
|
||
```sql
|
||
-- 需求 1 结果
|
||
create table top_ten_avg_ratings (
|
||
id int(11) not null AUTO_INCREMENT,
|
||
movie_id int(11) not null,
|
||
title varchar(200) not null,
|
||
avg_rating decimal(10, 2) not null,
|
||
update_time datetime default current_timestamp,
|
||
primary key (id),
|
||
unique key movie_id_unique (movie_id)
|
||
) ENGINE = InnoDB DEFAULT CHARSET = utf8;
|
||
```
|
||
|
||
> ⚠️ 实际库 / 表创建请先用 `create_table.sql` 跑一遍。
|
||
|
||
---
|
||
|
||
## 配置
|
||
|
||
`JDBCUtil.scala` 当前把连接信息硬编码:
|
||
|
||
```scala
|
||
private val user = "root"
|
||
private val password = "root123456"
|
||
private val url = "jdbc:mysql://192.168.48.10:3306/test"
|
||
```
|
||
|
||
**生产前必须改成外部配置**(application.conf / 环境变量 / 命令行参数)。详见 `CODE_REVIEW.md` 第 1 条。
|
||
|
||
`App.scala:14` 的 `master("local[2]")` 也是硬编码,集群提交前请改为从参数传入。
|
||
|
||
---
|
||
|
||
## 构建与运行
|
||
|
||
### 前置条件
|
||
|
||
- JDK 8
|
||
- Maven 3.6+
|
||
- MySQL 8.x(本地或局域网),能访问 `192.168.48.10:3306`
|
||
|
||
### 步骤
|
||
|
||
```powershell
|
||
# 1. 建表
|
||
mysql -h 192.168.48.10 -P 3306 -u root -p < src/main/resources/create_table.sql
|
||
|
||
# 2. 编译 + 打 fat-jar(assembly 插件已配置)
|
||
mvn -P dev clean package
|
||
|
||
# 3. dev profile:spark-core / spark-sql 是 compile 作用域,IDE 直接跑 App.main 即可
|
||
# build_pkg profile:provided 作用域,打成可提交到集群的 jar
|
||
|
||
# 4. 本地跑(也可直接 java -jar)
|
||
java -cp target/spark_project-1.0-SNAPSHOT-jar-with-dependencies.jar com.example.spark.App
|
||
```
|
||
|
||
### Maven Profile 说明
|
||
|
||
| profile | 用途 | Spark 依赖 scope |
|
||
| :-- | :-- | :-- |
|
||
| `dev` | IDE 本地跑、单元测试 | `compile` |
|
||
| `build_pkg` | 打胖包丢到集群跑 | `provided` |
|
||
|
||
---
|
||
|
||
## 已知问题 / TODO
|
||
|
||
详见 [`CODE_REVIEW.md`](CODE_REVIEW.md) 完整清单,优先级最高的:
|
||
|
||
- 🔴 MySQL 密码硬编码
|
||
- 🔴 `create_table.sql` 中 `avg_rating` 字段注释残留
|
||
- 🟠 `Entitys.scala` 死类 + 字段类型与 SQL 不一致
|
||
- 🟠 `insert2Mysql(res: Any)` 用 Any 做运行时分发
|
||
- 🟠 `SparkSession` 未 `stop()`;CSV `inferSchema=true`
|
||
- 🟡 无单元测试(JUnit 依赖已引但 `src/test/` 空)
|
||
|
||
---
|
||
|
||
## 参考
|
||
|
||
- MovieLens 数据集:<https://grouplens.org/datasets/movielens/>
|
||
- Spark SQL 文档(2.4.x):<https://spark.apache.org/docs/2.4.3/sql-programming-guide.html>
|
||
- HikariCP 配置:<https://github.com/brettwooldridge/HikariCP> |