从毕业设计到实战:手把手教你用Spark MLlib和SpringBoot搭建一个电商推荐系统(附完整源码)
·
从毕业设计到实战:手把手教你用Spark MLlib和SpringBoot搭建一个电商推荐系统(附完整源码)
1. 环境准备与架构设计
在开始构建电商推荐系统之前,我们需要先搭建好开发环境。以下是推荐的技术栈组合:
核心组件清单 :
- 数据处理引擎 :Spark 3.2.0(包含MLlib、Spark SQL、Spark Streaming)
- 后端框架 :SpringBoot 2.6.3
-
数据库
:
- MongoDB 5.0(主业务数据库)
- Redis 6.2(实时缓存)
- 消息队列 :Kafka 3.1.0
- 数据采集 :Flume 1.9.0
- 开发语言 :Scala 2.12 + Java 11
系统架构图 :
[用户端]
↓ HTTP
[SpringBoot服务] → [MongoDB]
↓ 日志
[Flume] → [Kafka] → [Spark Streaming]
↓ ↓
[Redis] [Spark MLlib]
提示:建议使用Docker容器化部署各组件,可大幅降低环境配置复杂度。例如通过docker-compose一键启动Kafka+Zookeeper集群。
2. 数据准备与特征工程
2.1 数据模型设计
我们需要三类核心数据表:
MongoDB集合设计 :
// 商品表
{
"productId": "B00006HAXW",
"name": "无线蓝牙耳机",
"categories": ["电子产品","音频设备"],
"tags": ["降噪","运动","Hi-Res"],
"price": 299.00
}
// 用户评分表
{
"userId": "U1001",
"productId": "B00006HAXW",
"rating": 4.5,
"timestamp": 1659321600
}
// 用户行为表
{
"userId": "U1001",
"productId": "B00006HAXW",
"behavior": "click", // 支持click/cart/purchase等
"duration": 30, // 停留时长(秒)
"timestamp": 1659321600
}
2.2 特征提取策略
对于协同过滤算法,我们需要构建用户-商品交互矩阵:
// Scala代码示例:构建评分矩阵
val ratings = spark.read.mongo("mongodb://localhost:27017/ecommerce.ratings")
.select($"userId", $"productId", $"rating")
.rdd
.map { row =>
(row.getAs[String]("userId").hashCode.toInt,
row.getAs[String]("productId").hashCode.toInt,
row.getAs[Double]("rating"))
}
特征权重分配表 :
| 行为类型 | 权重 | 说明 |
|---|---|---|
| 点击 | 0.3 | 表示初步兴趣 |
| 加购 | 0.6 | 强购买意向信号 |
| 购买 | 1.0 | 最明确的偏好表达 |
| 收藏 | 0.8 | 长期兴趣指标 |
3. 核心算法实现
3.1 离线推荐模块
ALS协同过滤实现
// 配置ALS参数
val als = new ALS()
.setRank(50) // 隐特征维度
.setMaxIter(20) // 迭代次数
.setRegParam(0.01) // 正则化参数
.setUserCol("userId")
.setItemCol("productId")
.setRatingCol("rating")
// 训练模型
val model = als.fit(training)
// 生成推荐
val userRecs = model.recommendForAllUsers(10) // 每个用户推荐10个商品
参数调优技巧 :
- 使用交叉验证确定最佳rank值:
val paramGrid = new ParamGridBuilder()
.addGrid(als.rank, Array(10, 50, 100))
.build()
val evaluator = new RegressionEvaluator()
.setMetricName("rmse")
val cv = new CrossValidator()
.setEstimator(als)
.setEvaluator(evaluator)
.setEstimatorParamMaps(paramGrid)
.setNumFolds(3)
val cvModel = cv.fit(training)
冷启动解决方案
对于新用户/新商品,采用混合推荐策略:
- 基于内容的推荐(商品标签匹配)
- 热门商品兜底
- 知识图谱关联推荐
# Python示例:内容相似度计算
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
product_descriptions = ["蓝牙 无线 耳机 降噪", ...]
vectorizer = TfidfVectorizer()
tfidf_matrix = vectorizer.fit_transform(product_descriptions)
cosine_sim = cosine_similarity(tfidf_matrix, tfidf_matrix)
3.2 实时推荐模块
实时处理流水线 :
用户行为 → Flume采集 → Kafka → Spark Streaming → 实时推荐引擎 → Redis
// Spark Streaming处理逻辑
val kafkaStream = KafkaUtils.createDirectStream[...](
ssc,
LocationStrategies.PreferConsistent,
ConsumerStrategies.Subscribe[String, String](topics, kafkaParams)
)
kafkaStream.map { record =>
// 解析用户行为
val (userId, productId, behavior) = parseLog(record.value())
// 从Redis获取用户最近行为
val recentActions = redisClient.lrange(s"recent:$userId", 0, 5)
// 计算实时推荐得分
calculateRealtimeScore(userId, productId, behavior, recentActions)
}
实时评分算法 :
实时得分 = 基础分 × 时间衰减系数 × 行为权重
其中:
- 基础分:ALS预测分或内容相似度分
- 时间衰减:1 / (1 + log(时间差))
- 行为权重:点击0.3,购买1.0等
4. 系统集成与API设计
4.1 SpringBoot接口设计
@RestController
@RequestMapping("/api/recommend")
public class RecommendController {
@GetMapping("/forUser")
public List<Product> getUserRecommendations(
@RequestParam String userId,
@RequestParam(defaultValue = "10") int size) {
// 从Redis获取实时推荐
List<String> realtimeRecs = redisTemplate.opsForList()
.range("rec:" + userId, 0, size - 1);
// 若实时推荐不足,补充离线推荐
if(realtimeRecs.size() < size) {
List<String> offlineRecs = mongoTemplate.find(
Query.query(Criteria.where("userId").is(userId))
.limit(size - realtimeRecs.size()),
UserRec.class);
realtimeRecs.addAll(offlineRecs);
}
return productService.getProducts(realtimeRecs);
}
}
4.2 性能优化方案
缓存策略 :
- 使用Redis缓存热门推荐结果
-
采用增量更新机制:
- 离线推荐每天全量更新
- 实时推荐每分钟增量更新
-
多级缓存设计:
@Cacheable(value = "userRecs", key = "#userId", unless = "#result == null || #result.size() < 5") public List<Recommendation> getRecommendations(String userId) { // ... }
Spark调优参数 :
spark.executor.memory=8G
spark.driver.memory=4G
spark.serializer=org.apache.spark.serializer.KryoSerializer
spark.sql.shuffle.partitions=200
5. 部署与监控
5.1 容器化部署
docker-compose.yml
示例片段:
services:
spark-master:
image: bitnami/spark:3.2.0
ports:
- "8080:8080"
environment:
- SPARK_MODE=master
spark-worker:
image: bitnami/spark:3.2.0
depends_on:
- spark-master
environment:
- SPARK_MODE=worker
- SPARK_MASTER_URL=spark://spark-master:7077
5.2 监控指标
需要监控的关键指标:
-
推荐质量 :
- 点击通过率(CTR)
- 转化率(CVR)
- 推荐多样性
-
系统性能 :
# Spark监控 curl http://spark-master:4040/api/v1/applications # Redis监控 redis-cli info stats | grep instantaneous_ops_per_sec -
业务看板 :
-- MongoDB聚合查询示例 db.ratings.aggregate([ { $match: { timestamp: { $gt: lastHour } } }, { $group: { _id: "$productId", avgRating: { $avg: "$rating" }, count: { $sum: 1 } }} ])
6. 完整源码结构
项目目录结构:
ecommerce-recommender/
├── spark-jobs/ # Spark作业
│ ├── offline/ # 离线推荐
│ └── streaming/ # 实时处理
├── recommender-api/ # SpringBoot服务
│ ├── src/
│ └── Dockerfile
├── docker-compose.yml # 容器编排
└── datasets/ # 示例数据
├── products.json
└── ratings.csv
关键代码文件:
-
ALSRecommender.scala- 离线推荐主逻辑 -
RealtimeStreaming.scala- 实时处理流水线 -
RecommendService.java- SpringBoot服务层 -
RecsysMonitor.java- 监控端点实现
提示:完整源码已托管在GitHub,包含详细配置说明和测试数据集,可通过仓库README获取部署指南。
7. 常见问题排查
问题1:Spark作业执行缓慢
-
检查数据倾斜:
ratings.groupBy("userId").count().orderBy($"count".desc).show(10) -
解决方案:
-
增加
spark.sql.shuffle.partitions - 对倾斜键单独处理
-
增加
问题2:实时推荐延迟高
-
优化检查点设置:
ssc.checkpoint("hdfs://checkpoints") -
调整Kafka消费参数:
spark.streaming.kafka.maxRatePerPartition=1000
问题3:推荐结果重复率高
-
引入多样性策略:
val diversifiedRecs = recs .groupBy(_.category) .flatMap { case (_, items) => items.take(2) // 每类最多取2个 } .take(10)
在实际部署过程中,建议先使用小规模数据进行端到端测试,验证各组件连通性后再逐步放大数据规模。对于生产环境,需要考虑添加灰度发布和A/B测试能力来验证推荐效果。
更多推荐

所有评论(0)