这个一个老生常谈的系统设计话题,不妨再回顾一下。
当你打开 Twitter、微博或者小红书,滑动手机屏幕,一条条动态实时出现在你眼前——这个看似简单的”刷微博”动作,背后是每秒处理数十万条动态生成的复杂系统。本文深入解析 Feed 流系统的核心架构,从推模式到拉模式,从缓存策略到分页设计,完整呈现这个社交平台核心基础设施的设计之道。
引言:Feed 流系统是什么?
Feed 流(信息流)是社交平台的核心功能:
- Twitter:你关注的人的推文
- 微博:你关注的大V的博文
- 小红书:你关注的博主的笔记
- 抖音/快手:算法推荐给你的短视频
核心问题:如何从数以亿计的内容中,筛选出用户想看的,并按时序呈现?
一、核心概念与模型
1.1 Feed 流的核心实体
flowchart TB
subgraph Entity["核心实体"]
User["用户
User"]
Content["内容
Post/Tweet"]
Follow["关注关系
Follow"]
Timeline["时间线
Timeline"]
end
User -->|"发布"| Content
User -->|"关注"| User
User -->|"拉取"| Timeline
Content -->|"推送至"| Timeline
1.2 数据模型设计
1 | -- 用户表 |
二、核心架构:推 vs 拉
2.1 两种模式对比
flowchart TB
subgraph Push["推模式 (Push)
写入时计算"]
Post["发布内容"]
PushService["推模式服务"]
FollowerList["粉丝列表"]
Fanout["Fanout Service"]
FeedTable["每个粉丝的
Feed 表"]
Post --> PushService
PushService --> Fanout
Fanout --> FollowerList
FollowerList --> FeedTable
end
subgraph Pull["拉模式 (Pull)
读取时计算"]
Request["用户请求"]
PullService["拉取服务"]
FollowList["关注列表"]
PostTable["内容表"]
Merge["合并排序"]
Result["返回结果"]
Request --> PullService
PullService --> FollowList
FollowList --> PostTable
PostTable --> Merge
Merge --> Result
end
2.2 推模式 (Push)
核心思想:当用户发布内容时,立即计算并写入所有粉丝的 Feed 表
1 | class PushService: |
优点:
- 读取时速度快,不需要计算
- 适合粉丝数量少的用户
缺点:
- 粉丝多时写入压力大(Fanout 爆炸)
- 新用户关注列表很大时体验差
2.3 拉模式 (Pull)
核心思想:用户请求 Feed 时,实时查询关注列表的内容并排序
1 | class PullService: |
优点:
- 写入压力小
- 数据实时(不会有延迟)
缺点:
- 读取时需要大量计算
- 关注列表大时延迟高
2.4 混合模式 (Hybrid)
业界最佳实践:Twitter、微博采用混合模式
flowchart TB
subgraph Hybrid["混合模式"]
UserType{"用户类型"}
Influencer["大V
(粉丝>10万)"]
Normal["普通用户
(粉丝<10万)"]
Influencer -->|"推模式
(预计算)"| Cache["Redis 缓存"]
Normal -->|"拉模式
(实时计算"| Cache
Cache -->|"统一返回"| Result
end
策略:
- 大 V:使用推模式,发布时预计算到 Fanout Cache
- 普通用户:使用拉模式,读取时实时计算
- 混合:根据用户粉丝数量动态选择
三、核心模块设计
3.1 Fanout Service(推模式核心)
1 | public class FanoutService { |
3.2 Timeline Service(读取服务)
1 | class TimelineService: |
3.3 分页设计
sequenceDiagram
participant Client as 客户端
participant API as Timeline API
participant Redis as Redis Cache
participant DB as Database
Client->>API: 请求第 1 页 (cursor=0)
API->>Redis: ZREVRANGE feed:123 0 19
Redis-->>API: 返回 20 条 + cursor=1700000001000
API-->>Client: 返回数据 + next_cursor
Note over Client: 用户滑动到第 2 页
Client->>API: 请求第 2 页 (cursor=1700000001000)
API->>Redis: ZREVRANGE feed:123 1700000000999 1700000001000
Redis-->>API: 返回 20 条
API-->>Client: 返回数据
游标分页 vs 偏移分页:
| 方式 | 优点 | 缺点 |
|---|---|---|
| 游标 (Cursor) | 性能稳定,不随页数增加而变慢 | 不能跳页 |
| 偏移 (Offset) | 可以跳页 | 页数大时性能差 |
四、缓存策略
4.1 多级缓存架构
flowchart TB
subgraph Cache["多级缓存"]
L1["L1 本地缓存
Guava/Caffeine
1分钟 TTL"]
L2["L2 Redis 缓存
热点用户 Feed
5分钟 TTL"]
L3["L3 数据库
兜底"]
end
Request --> L1
L1 -->|"命中"| Response
L1 -->|"未命中"| L2
L2 -->|"命中"| Response
L2 -->|"未命中"| L3
L3 --> Response
4.2 缓存预热
1 | class CacheWarmer: |
五、高并发优化
5.1 读写分离
flowchart LR
subgraph Write["写入"]
WriteAPI["写接口"] --> Primary["主库"]
end
subgraph Read["读取"]
ReadAPI["读接口"] --> Replica1["从库 1"]
ReadAPI --> Replica2["从库 2"]
ReadAPI --> Replica3["从库 N"]
end
5.2 热点用户处理
1 | class InfluencerService: |
六、排序与个性化
6.1 基础排序
1 | def basic_sort(posts: List[Post]) -> List[Post]: |
6.2 智能排序
1 | def smart_sort(posts: List[Post], user_id: int) -> List[Post]: |
七、总结
7.1 架构选型
| 模式 | 适用场景 | 代表产品 |
|---|---|---|
| 推模式 | 粉丝少、内容多 | 微信公众号 |
| 拉模式 | 粉丝多、内容少 | 微博(早期) |
| 混合模式 | 通用 | Twitter、微博 |
7.2 核心设计原则
- 读写分离:写入推模式,读取用缓存
- 分级处理:大 V 单独处理,避免 Fanout 爆炸
- 降级兜底:缓存失效时降级到数据库
- 分页游标:避免深度分页性能问题
7.3 技术选型
| 模块 | 技术选型 |
|---|---|
| 缓存 | Redis Cluster |
| 消息队列 | Kafka |
| 存储 | MySQL + HBase |
| 搜索 | Elasticsearch |
一句话总结:Feed 流系统的核心是用空间换时间(预计算)、用缓存换性能(多级缓存)、用降级换可用(容灾方案)。