电影推荐系统-后端
项目演示视频地址:https://www.bilibili.com/video/BV1tm4y1h7EQ/
服务名 | 功能描述 |
---|---|
搜索服务 | 使用ElasticSearch实现电影的多字段模糊搜索 |
推荐服务 | 使用Spark实现电影的离线推荐和实时推荐 |
电影详情服务 | 获取电影的具体信息、评论评分 |
用户服务 | 用户登录、注册 |
短信邮件服务 | 使用短信或邮件发送验证码 |
网关服务 | 请求分发、鉴权 |
MySQL-5.7.22
表数据量:1050170
对评论表中的douban_id字段加上普通索引,适用场景:用户查看某电影的评论
SELECT
review_id,
douban_id,
……
FROM
movie_recommendation.movie_reviews
WHERE
douban_id = 1291543
ORDER BY
user_movie_rating_agree DESC,
user_movie_rating_time DESC
使用索引前(10次平均/ms) | 使用索引后(10次平均/ms) |
---|---|
58ms(第一次查询时4.434s) | 18ms |
第一次很慢的原因,全表扫描;
第二次之后快是由于MySQL将查询语句和结果放到了缓存中
表数据量:8715
对电影表中的title字段加上普通索引,适用场景:用户填写关键字,查询相关电影的详情
SELECT
douban_id,
title,
……
FROM
movie_recommendation.movie_detail
WHERE
title like '星际%';
使用索引前(10次平均/ms) | 使用索引后(10次平均/ms) |
---|---|
42ms | 22ms |
需求:
用户输入一些关键字去搜索,返回在电影名、演员、导演与之匹配的结果。
SELECT
douban_id,
title,
……
FROM
movie_recommendation.movie_detail
WHERE
title like '%星际%' or casts like '%星际%' or directors like '%星际%';
问题:
{
"query": {
"multi_match": {
"query": "星际",
"minimum_should_match": "80%", //查询结果至少要包含【分次数*80%】向下取整的数量
"fields": ["title^5","casts^3","directors^2"] // 查询字段并添加权重
}
}
}
结果:
平均用时:7ms,提升巨大!
优势:
问题:
解决方案:
配置Canal中遇到的问题:
配置adapter的application.yml文件
outerAdapters的name要指明es6还是es7;
mode为rest时hosts要加http://
注意yml文件格式
配置adapter的映射文件时:
评分表(movie_user_ratings)和评论表(movie_reviews)中存放了所有电影的评分和评论,其中的数据在不断增长,并且很容易增长到一个很夸张的地步,因为一门热门电影就可能有几百万的评分数,如下所示。因此,只用单表存储数据量会非常之大。
这里先往这两张表中各插入2000w条数据,先观察查询性能下降情况。
简单查询,能用到索引下推:
SELECT
review_id,
douban_id,
……
FROM
movie_recommendation.movie_reviews
WHERE
douban_id = 1291543
ORDER BY
user_movie_rating_agree DESC,
user_movie_rating_time DESC
数据量:105w | 数据量:2100w |
---|---|
18ms | 21ms |
可见性能并没有下降的很厉害,我认为原因是:IO次数并没有增加!
对于二级索引,B+树中非叶子节点中占20+6+4=30字节,叶子节点占20+4=24字节,每页可装161024/24=682条记录,可装161024/30=546个页号,3层B+树可装:546546642=1.9e,远大于2000w的数据量,3次IO查到主键,在回表查询完整记录。
对于主键索引,每条记录约210字节,主键+指针=20+6=26字节,每页可装161024/26=630个页号,每页可装161024/210=78条记录,3层B+树可装63063078=3000w,大于目前的2000w数据量,也就是还可以3次IO查到完整数据,和原来100w时的数据量一样。如果最终数据量超过了3000w,那么需要4层B+树来存储,会增加IO次数,性能才会下降。
SELECT
douban_id,
sum( CASE WHEN user_movie_rating > 40 THEN 1 ELSE 0 END ) / count( douban_id ) AS positive
FROM
movie_reviews
GROUP BY
douban_id;
数据量:105w | 数据量:2100w |
---|---|
16.3s | 498s |
使用mycat2对评分表(movie_user_ratings)和评论表(movie_reviews)进行分库和分表。
分库分表策略:
使用mycat的mod_hash方法,以douban_id作为分库键和分表键。
分成2个库,分别在两个虚拟机的MySQL中(配置了主从复制和读写分离),每个库中10张表。
使用mycat分库分表后,计算好评率:
SELECT
douban_id,
sum( CASE WHEN user_movie_rating > 40 THEN 1 ELSE 0 END ) / count( douban_id ) AS positive
FROM
movie_reviews
GROUP BY
douban_id;
数据量:105w | 数据量:2100w |
---|---|
1.462s | 未测试,磁盘空间不够 |
离线推荐算法计算的是用户的所有历史数据,十分耗时,难以做到实时响应,因此需要离线运行,并且需要按时运行以更新推荐结果集。离线推荐算法反映的是用户整个历史的电影喜好。
先要找出该用户的所有未评分电影,两两计算此电影与任一未评分电影间的相似度,得到电影间的相似度列表并将其降序排列,选取相似度较高的未评分电影即可作为该电影的相似电影列表。
实时计算与离线计算在推荐系统上的不同之处在于实时计算推荐结果反映最近一段时间用户近期的偏好,而离线推荐结果反映用户历史总体的偏好。
设计实时推荐算法有三点原因:
此处可能存在不合适展示的内容,页面不予展示。您可通过相关编辑功能自查并修改。
如您确认内容无涉及 不当用语 / 纯广告导流 / 暴力 / 低俗色情 / 侵权 / 盗版 / 虚假 / 无价值内容或违法国家有关法律法规的内容,可点击提交进行申诉,我们将尽快为您处理。