外观
一句话答案
Feed 流推模式(写扩散/适合粉丝少)和拉模式(读扩散/适合大 V),实际混合使用,Redis ZSet 存储。
核心要点
Feed 流的两种模式:
推模式(Push / Fan-out on Write):
用户发帖 → 立即推送到所有关注者的 Feed 列表(写时扩散)
优点:读取 Feed 直接从自己的列表取,O(1) 读
缺点:大 V(百万粉丝)发帖时,需要写入百万个列表,写入放大严重
实现:
LPUSH feed:user:1002 postId # 推给关注者 1002
LPUSH feed:user:1003 postId # 推给关注者 1003
...(百万次 Redis 写)
拉模式(Pull / Fan-out on Read):
用户查看 Feed → 实时查询所有关注者的最新帖子(读时合并)
优点:写入简单(只写自己的帖子列表),不依赖关注者数量
缺点:读取时需要合并多个关注者的帖子(可能几百个关注者),读放大严重
实现:
ZREVRANGE posts:user:大V 0 10 # 读取每个关注者的最新10条
# 在内存中合并排序,取 top N
推拉结合(微博/朋友圈实际方案):
普通用户:推模式(关注者少,写放大可接受)
大 V / 热门账号:拉模式(粉丝千万,不做推)
读取时:自己的推 Feed + 关注大V的实时拉取,在应用层合并Redis 单机瓶颈优化:
问题:
Feed 列表数据量大(每个用户一个 ZSet/List),总 key 数量极多
热点用户的 Feed List 被频繁读写(热 key)
解决方案:
1. Redis Cluster(分片扩展容量)
→ 按用户 ID hash 将 key 分散到不同节点
→ 单节点压力降低(线性扩展)
→ 注意:Feed List 和用户基本信息可能在不同节点(跨 slot 操作需特殊处理)
2. 热 key 拆分(大 V 的 Feed 推送)
→ 大 V 的关注者 Feed 列表分多个 key 存储
→ feed:user:1002:shard:0 / shard:1 / shard:2
→ 读取时聚合多个分片
3. 本地缓存(Caffeine)
→ 热门 Feed 在应用层本地缓存(5s TTL)
→ 极热数据(如首页 Feed)不每次都打 Redis
4. 分级存储
→ 最近 30 天的 Feed 存 Redis(快速读取)
→ 更早的帖子存 MySQL / HBase(历史翻页时再查)七、日志与大数据
追问与易错
追问方向:
- 推拉混合怎么实现?
- 新关注加载历史动态?
- 存储选型?
易错点:
- ❌ 所有人都用推模式——大 V 粉丝多推模式写爆炸
- ❌ 忽略分页问题——用 score 游标而非 offset
💡 记忆锚点
Feed流核心是推拉抉择:推模式=发帖时写到每个粉丝的收件箱(写扩散,读O(1)但大V粉丝百万就写爆了);拉模式=刷Feed时实时去每个关注人的发件箱取(读扩散,写简单但关注多了读慢)。实战推拉结合:普通用户推,大V拉,应用层合并。Redis ZSet存Feed用score做游标分页。