短视频推荐算法总翻车?这6个架构绝招让抖音推荐丝滑如德芙!
短视频推荐算法总翻车?这6个架构绝招让抖音推荐丝滑如德芙!
大家好,今天咱们聊一个让无数程序员秃头的问题——如何设计一个能扛住亿级用户的短视频推荐系统。
别觉得这是抖音、快手的专属难题。现在是个APP都想做短视频,用户刷视频跟呼吸一样频繁,推荐系统一崩,用户分分钟卸载。今天我就用大白话+实战案例,给你讲清楚这个让人又爱又恨的系统到底怎么玩。
一、短视频推荐系统的4大"死亡陷阱"
先别急着写代码,咱们得先搞清楚这玩意儿为啥这么难搞。我总结了下,主要有4个坑,每个都能让你半夜三点被老板电话叫醒。
1. 数据量爆炸:每天新增PB级数据
你知道抖音每天产生多少视频吗?说出来吓死你——上亿条!每条视频从上传、转码、审核到推荐,全流程数据加起来就是PB级别。
这还不算用户的观看行为、点赞、评论、分享...这些数据加起来,MySQL直接原地去世。我亲眼见过一个创业公司的推荐系统,上线第一天就被2TB的数据打趴下了。
2. 实时性要求:用户等不了3秒
现在的用户都被惯坏了,刷视频必须秒开,推荐必须秒出。你要是让用户等3秒才出推荐,人家直接卸载。
更变态的是,用户刚点赞了一个猫猫视频,下一条就必须是猫猫,延迟不能超过1分钟。这种实时性要求,比股票交易系统还苛刻。
3. 冷启动问题:新用户新视频怎么推
新用户注册,你对他一无所知,怎么推荐?新视频上传,没有任何用户行为,怎么判断质量?
我见过最惨的案例:某短视频APP,新用户注册后推荐的全是半年前的老视频,用户留存率直接跌到10%以下。
4. 推荐效果:既要又要还要
推荐系统最难的是平衡:
- 既要用户感兴趣(点击率)
- 又要让用户停留时间长(完播率)
- 还要让用户互动(点赞评论分享)
- 最后还得考虑内容多样性,不能总推一类
这就像让你同时追4个女朋友,每个都得哄好,难度系数直接爆表。
二、能扛亿级用户的6层架构设计
说了这么多坑,那到底怎么设计才能扛住亿级用户?我结合参与过的某头部短视频平台架构改造案例,给你整一套能落地的方案。
1. 第一层:数据采集层 - 用户行为全量收集
推荐系统的核心是数据,我们先搞定数据采集:
// 用户行为数据采集服务
@Service
public class UserActionCollector {
@Autowired
private KafkaProducer<String, String> kafkaProducer;
// 收集用户行为,异步发送到Kafka
public void collectAction(UserAction action) {
String message = JSON.toJSONString(action);
kafkaProducer.send(new ProducerRecord<>("user-actions", message));
}
}
// 用户行为数据结构
@Data
public class UserAction {
private Long userId; // 用户ID
private Long videoId; // 视频ID
private String actionType; // 行为类型:view/like/share/comment
private Long duration; // 观看时长
private Long timestamp; // 时间戳
private Map<String, Object> context; // 上下文信息
}
核心要点:
- 全量收集:播放、点赞、分享、评论、完播率...一个都不能少
- 异步发送:用Kafka削峰,避免影响主流程
- 实时+离线:实时数据用于在线推荐,离线数据用于模型训练
2. 第二层:特征工程层 - 把原始数据变成AI能懂的特征
原始数据不能直接喂给算法,得加工成特征:
# 用户特征提取
class UserFeatureExtractor:
def extract_user_features(self, user_id):
features = {}
# 用户画像特征
features['user_id'] = user_id
features['register_days'] = self.get_register_days(user_id)
features['total_watch_time'] = self.get_total_watch_time(user_id)
features['like_rate'] = self.get_like_rate(user_id)
# 兴趣标签
features['interest_tags'] = self.get_interest_tags(user_id)
# 时间偏好
features['active_hours'] = self.get_active_hours(user_id)
return features
# 视频特征提取
class VideoFeatureExtractor:
def extract_video_features(self, video_id):
features = {}
# 基础特征
features['video_id'] = video_id
features['duration'] = self.get_duration(video_id)
features['category'] = self.get_category(video_id)
features['tags'] = self.get_tags(video_id)
# 统计特征
features['view_count'] = self.get_view_count(video_id)
features['like_rate'] = self.get_like_rate(video_id)
features['completion_rate'] = self.get_completion_rate(video_id)
return features
核心要点:
- 用户特征:包括画像、兴趣、行为偏好、时间习惯等
- 视频特征:包括内容、质量、热度、时效性等
- 上下文特征:时间、地点、设备、网络环境等
3. 第三层:召回层 - 从千万视频中找出候选集
直接对所有视频计算推荐分数不现实,我们先召回候选集:
// 多路召回服务
@Service
public class RecallService {
// 协同过滤召回
public List<Long> collaborativeFilteringRecall(Long userId, int size) {
// 基于用户相似度的协同过滤
return userCFService.recall(userId, size);
}
// 内容召回
public List<Long> contentBasedRecall(Long userId, int size) {
// 基于用户兴趣标签的内容召回
return contentService.recall(userId, size);
}
// 热门召回
public List<Long> hotRecall(int size) {
// 基于热度的召回
return hotService.getHotVideos(size);
}
// 冷启动召回
public List<Long> coldStartRecall(Long userId, int size) {
// 新用户的冷启动召回
return coldStartService.recall(userId, size);
}
// 融合多路召回结果
public List<Long> multiPathRecall(Long userId, int size) {
List<Long> candidates = new ArrayList<>();
candidates.addAll(collaborativeFilteringRecall(userId, size/4));
candidates.addAll(contentBasedRecall(userId, size/4));
candidates.addAll(hotRecall(size/4));
candidates.addAll(coldStartRecall(userId, size/4));
return candidates.stream().distinct().collect(Collectors.toList());
}
}
核心要点:
- 多路召回:协同过滤+内容召回+热门召回+冷启动
- 分层过滤:先召回1000个候选,再精排100个,最后重排10个
- 实时+离线:实时召回保证时效性,离线召回保证准确性
4. 第四层:排序层 - 给候选视频打分排序
# 深度学习排序模型
import tensorflow as tf
class DeepFMModel:
def __init__(self):
self.model = self.build_model()
def build_model(self):
# 输入层
user_id = tf.keras.layers.Input(shape=(1,), name='user_id')
video_id = tf.keras.layers.Input(shape=(1,), name='video_id')
# Embedding层
user_embedding = tf.keras.layers.Embedding(
input_dim=1000000, output_dim=64, name='user_embedding'
)(user_id)
video_embedding = tf.keras.layers.Embedding(
input_dim=10000000, output_dim=64, name='video_embedding'
)(video_id)
# 特征拼接
concat = tf.keras.layers.concatenate([
tf.keras.layers.Flatten()(user_embedding),
tf.keras.layers.Flatten()(video_embedding)
])
# 深度网络
dense1 = tf.keras.layers.Dense(128, activation='relu')(concat)
dense2 = tf.keras.layers.Dense(64, activation='relu')(dense1)
# 输出层
output = tf.keras.layers.Dense(1, activation='sigmoid', name='ctr')(dense2)
model = tf.keras.Model(inputs=[user_id, video_id], outputs=output)
model.compile(optimizer='adam', loss='binary_crossentropy')
return model
def predict(self, user_id, video_ids):
# 批量预测CTR
predictions = self.model.predict({
'user_id': [user_id] * len(video_ids),
'video_id': video_ids
})
return predictions.flatten()
核心要点:
- 多目标排序:CTR、完播率、互动率、时长等
- 深度学习:Wide&Deep、DeepFM、DIN等模型
- 在线学习:实时更新模型,适应用户兴趣变化
5. 第五层:重排层 - 保证多样性和新鲜度
// 重排服务
@Service
public class ReRankService {
public List<Video> reRank(List<Video> rankedVideos, Long userId) {
List<Video> finalList = new ArrayList<>();
// 1. 多样性打散
Set<String> categories = new HashSet<>();
for (Video video : rankedVideos) {
if (categories.size() < 3 || !categories.contains(video.getCategory())) {
finalList.add(video);
categories.add(video.getCategory());
}
}
// 2. 新鲜度加权
finalList.sort((a, b) -> {
double scoreA = a.getScore() * getFreshnessBoost(a.getCreateTime());
double scoreB = b.getScore() * getFreshnessBoost(b.getCreateTime());
return Double.compare(scoreB, scoreA);
});
// 3. 曝光过滤
finalList = filterExposedVideos(finalList, userId);
return finalList.subList(0, Math.min(10, finalList.size()));
}
private double getFreshnessBoost(long createTime) {
long hours = (System.currentTimeMillis() - createTime) / (1000 * 3600);
return Math.exp(-hours / 24.0); // 24小时衰减
}
}
核心要点:
- 多样性:避免连续推荐同类内容
- 新鲜度:新视频加权,老视频降权
- 曝光过滤:避免重复推荐用户看过的
6. 第六层:存储层 - 支持高并发读写
# Redis集群配置
spring:
redis:
cluster:
nodes:
- 10.0.1.1:6379
- 10.0.1.2:6379
- 10.0.1.3:6379
max-redirects: 3
timeout: 3000ms
lettuce:
pool:
max-active: 1000
max-idle: 100
min-idle: 50
# MySQL分库分表配置
sharding:
jdbc:
datasource:
names: ds0,ds1,ds2,ds3
ds0:
url: jdbc:mysql://10.0.1.1:3306/video_0
ds1:
url: jdbc:mysql://10.0.1.2:3306/video_1
ds2:
url: jdbc:mysql://10.0.1.3:3306/video_2
ds3:
url: jdbc:mysql://10.0.1.4:3306/video_3
config:
sharding:
tables:
video:
actual-data-nodes: ds$->{0..3}.video_$->{0..63}
table-strategy:
inline:
sharding-column: video_id
algorithm-expression: video_$->{video_id % 64}
database-strategy:
inline:
sharding-column: video_id
algorithm-expression: ds$->{video_id % 4}
核心要点:
- Redis集群:缓存用户画像、实时特征
- MySQL分库分表:存储视频基础信息、用户行为
- HBase:存储用户行为日志,支持海量写入
- ES:视频搜索,支持复杂查询
三、实战案例:某短视频APP从0到1亿的架构演进
说了这么多理论,给你讲个真实案例。我参与的某短视频APP,从日活10万到1亿的架构演进过程。
1. 初始阶段:单体架构(日活10万)
刚开始用户量小,直接用最简单的架构:
// 最简单的推荐接口
@RestController
public class SimpleRecommendController {
@Autowired
private VideoService videoService;
@GetMapping("/recommend")
public List<Video> recommend(@RequestParam Long userId) {
// 1. 查用户喜欢的分类
List<String> categories = userService.getUserCategories(userId);
// 2. 按分类+热度排序
return videoService.getVideosByCategories(categories);
}
}
问题:
- 数据库查询慢,用户等10秒才出推荐
- 新用户推荐质量差,留存率只有20%
- 单机MySQL,存储快满了
2. 优化阶段:引入缓存+简单算法(日活100万)
用户量上来后,我们做了第一次优化:
// 引入Redis缓存
@Service
public class CacheRecommendService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public List<Video> recommend(Long userId) {
String key = "recommend:" + userId;
// 1. 先查缓存
List<Video> cached = (List<Video>) redisTemplate.opsForValue().get(key);
if (cached != null) {
return cached;
}
// 2. 计算推荐
List<Video> videos = calculateRecommend(userId);
// 3. 缓存5分钟
redisTemplate.opsForValue().set(key, videos, 5, TimeUnit.MINUTES);
return videos;
}
}
效果:
- 响应时间从10秒降到500ms
- 数据库压力降低80%
- 但还是扛不住更大的量
3. 分布式阶段:微服务+机器学习(日活1000万)
用户量突破1000万,我们做了彻底的重构:
# Kubernetes部署配置
apiVersion: apps/v1
kind: Deployment
metadata:
name: recommend-service
spec:
replicas: 50
selector:
matchLabels:
app: recommend-service
template:
metadata:
labels:
app: recommend-service
spec:
containers:
- name: recommend-service
image: recommend:latest
ports:
- containerPort: 8080
env:
- name: JAVA_OPTS
value: "-Xmx4g -Xms4g"
resources:
requests:
memory: "4Gi"
cpu: "2000m"
limits:
memory: "4Gi"
cpu: "2000m"
架构升级:
- 微服务拆分:召回、排序、重排独立部署
- 机器学习:用TensorFlow训练CTR模型
- Redis集群:16个节点,支持100万QPS
- 数据库分库分表:16个库,每个库64张表
效果:
- 支持1000万日活,50万QPS
- 推荐准确率提升40%
- 用户留存率从20%提升到45%
4. 亿级阶段:AI+大数据平台(日活1亿)
最后用户量突破1亿,我们搭建了完整的AI平台:
# 实时特征计算
class RealtimeFeatureService:
def __init__(self):
self.redis_client = redis.RedisCluster(
startup_nodes=[
{"host": "10.0.1.1", "port": 6379},
{"host": "10.0.1.2", "port": 6379}
]
)
def get_user_features(self, user_id):
# 实时用户特征
features = {}
# 从Redis获取实时特征
features['realtime_interests'] = self.redis_client.hget(
f"user_features:{user_id}", "interests"
)
# 计算实时偏好
recent_actions = self.redis_client.lrange(
f"user_actions:{user_id}", 0, 100
)
features['recent_behavior'] = self.analyze_behavior(recent_actions)
return features
最终架构:
- 深度学习:DIN+DeepFM混合模型
- 实时计算:Flink实时特征计算
- 多级缓存:Redis+本地缓存+CDN
- AB测试:同时运行多个算法版本
核心指标:
- 日活:1.2亿
- 日均视频播放:500亿次
- 推荐服务QPS:200万
- 推荐延迟:P99 < 100ms
四、7个避坑指南(血与泪的教训)
最后,给你总结7个我踩过的坑,每个都是白花花的银子换来的。
1. 推荐结果不更新?缓存时间惹的祸
问题:用户反馈推荐内容万年不变,后来发现是缓存时间设置太长。
解决方案:
- 用户特征缓存:5分钟
- 热门视频缓存:1小时
- 新视频缓存:实时更新
// 智能缓存策略
public class SmartCache {
public void setVideoCache(Long videoId, Video video) {
if (video.getCreateTime() > System.currentTimeMillis() - 3600000) {
// 新视频,缓存5分钟
redisTemplate.opsForValue().set("video:" + videoId, video, 5, TimeUnit.MINUTES);
} else {
// 老视频,缓存1小时
redisTemplate.opsForValue().set("video:" + videoId, video, 1, TimeUnit.HOURS);
}
}
}
2. 冷启动用户流失?新用户专属策略
问题:新用户推荐质量差,3日留存只有15%。
解决方案:
- 新用户前10条视频:人工精选+热门视频
- 收集10个行为后:启动个性化推荐
- 地理位置:优先推荐本地热门
3. 推荐重复?曝光过滤要做好
问题:用户刷到重复视频,体验极差。
解决方案:
- BloomFilter存储用户曝光历史
- 滑动窗口:最近1000条曝光记录
- 实时去重:推荐结果实时过滤
4. 推荐系统崩溃?限流降级不能少
问题:618大促,推荐系统被流量冲垮。
解决方案:
- 接口限流:每秒最多1000次请求
- 降级策略:Redis挂了走本地缓存
- 熔断机制:错误率超过5%直接降级
5. 推荐效果差?特征工程是关键
问题:模型AUC只有0.6,推荐效果跟瞎猜差不多。
解决方案:
- 用户特征:增加用户活跃度、设备类型
- 视频特征:增加视频清晰度、音乐类型
- 交叉特征:用户×视频的交叉特征
6. 存储成本爆炸?冷热分离要趁早
问题:用户行为数据存储成本每月100万。
解决方案:
- 热数据:最近7天,Redis+MySQL
- 温数据:7天-3个月,MySQL+压缩
- 冷数据:3个月以上,HDFS+Parquet
7. 推荐算法偏见?多样性算法要平衡
问题:用户兴趣越来越窄,形成信息茧房。
解决方案:
- 探索策略:10%的推荐用随机探索
- 多样性打分:视频相似度惩罚
- 时间衰减:用户兴趣随时间衰减
总结:推荐系统的3个核心思维
设计一个亿级用户的短视频推荐系统,技术只是基础,更重要的是思维:
- 数据思维:一切用数据说话,不要拍脑袋决策
- 用户思维:推荐的是用户想看的,不是你想推的
- 迭代思维:持续优化,永远没有完美的推荐系统
最后说一句:推荐系统是个慢活,别指望一蹴而就。我见过的成功推荐系统,都是3分技术+7分运营,持续优化出来的。
关注我,不迷路,持续分享后端技术干货!
点赞、评论、转发,是我创作的最大动力!
公众号:服务端技术精选
标题:短视频推荐算法总翻车?这6个架构绝招让抖音推荐丝滑如德芙!
作者:jiangyi
地址:http://jiangyi.space/articles/2025/12/21/1766304294399.html
- 一、短视频推荐系统的4大"死亡陷阱"
- 1. 数据量爆炸:每天新增PB级数据
- 2. 实时性要求:用户等不了3秒
- 3. 冷启动问题:新用户新视频怎么推
- 4. 推荐效果:既要又要还要
- 二、能扛亿级用户的6层架构设计
- 1. 第一层:数据采集层 - 用户行为全量收集
- 2. 第二层:特征工程层 - 把原始数据变成AI能懂的特征
- 3. 第三层:召回层 - 从千万视频中找出候选集
- 4. 第四层:排序层 - 给候选视频打分排序
- 5. 第五层:重排层 - 保证多样性和新鲜度
- 6. 第六层:存储层 - 支持高并发读写
- 三、实战案例:某短视频APP从0到1亿的架构演进
- 1. 初始阶段:单体架构(日活10万)
- 2. 优化阶段:引入缓存+简单算法(日活100万)
- 3. 分布式阶段:微服务+机器学习(日活1000万)
- 4. 亿级阶段:AI+大数据平台(日活1亿)
- 四、7个避坑指南(血与泪的教训)
- 1. 推荐结果不更新?缓存时间惹的祸
- 2. 冷启动用户流失?新用户专属策略
- 3. 推荐重复?曝光过滤要做好
- 4. 推荐系统崩溃?限流降级不能少
- 5. 推荐效果差?特征工程是关键
- 6. 存储成本爆炸?冷热分离要趁早
- 7. 推荐算法偏见?多样性算法要平衡
- 总结:推荐系统的3个核心思维