最近开发遇到一个场景,记录一下。 场景:用户可以举报别人或者某个评论,管理员处理举报,系统将处理的反馈发送给所有举报者。 这个,仔细一想,不就是经典的社交平台信息流(Feed)系统设计问题吗?尤其是当涉及到「高影响力用户」(如被几百万人关注的用户)发布内容时,系统需要高效地将该内容分发到大量用户的 Feed 中,确保性能和延迟可控。
整个场景,可以简化为如下:
- 系统有用户user;
- 用户间可以相互关注;
- 用户可以发布 Post;
- 用户在信息流中,可以看到自己关注的人发布的Post;
- 场景是,如果一个用户被很多人关注了(比如几百万人),这个用户发布一个 Post,如何有效的展示在这几百万人的信息流中呢?
以下是简单的系统设计。惯例,我设计完,让 AI代写,然后 Review。
🎯 目标
设计一个支持高并发、高性能、可扩展的社区系统,重点解决信息流分发的问题,尤其是粉丝数巨大的用户发帖时,系统如何高效地将内容推送到其粉丝的 Feed。
🔧 技术栈回顾
- 前端: React
- 后端: Golang
- 数据库: MySQL + Redis
✍️ 基础数据模型设计
用户表(User)
CREATE TABLE users (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
username VARCHAR(255) UNIQUE,
created_at DATETIME
);
关注关系表(Follow)
CREATE TABLE follows (
follower_id BIGINT,
followee_id BIGINT,
created_at DATETIME,
PRIMARY KEY(follower_id, followee_id),
INDEX(followee_id)
);
帖子表(Post)
CREATE TABLE posts (
id BIGINT PRIMARY KEY AUTO_INCREMENT,
user_id BIGINT,
content TEXT,
created_at DATETIME,
INDEX(user_id, created_at)
);
🚩 信息流系统设计:核心问题
当一个用户(如明星)有几百万粉丝时,如果他发一条 Post,你不能同步地写入几百万条数据(推模式),否则会造成:
- 数据库写入压力过大
- 用户体验延迟高
- 系统扩展性差
✅ 信息流分发策略
1️⃣ 拉模式(Pull Model)
思路:用户打开信息流时,实时去查询他关注的用户的最新 Post。
实现方式:
- 用户打开 Feed 页面时,查询他关注的人 ID 列表
- 然后去查这些人最近发布的 Post(比如最近 N 条)
- 将多个用户的 Post 合并排序(通常按时间)
优点:
- 写入成本低(用户发 Post 时无需写入粉丝 Feed)
- 数据新鲜,实时性强
缺点:
- 读时开销大,尤其是用户关注几千人时
- 查询慢,分页困难
- 不适合用户量大、关注量大的场景
2️⃣ 推模式(Push Model)
思路:用户发布 Post 时,系统将该 Post 推送到所有关注他的人的 Feed 中。
实现方式:
- Post 写入后,遍历关注该用户的所有粉丝,将 Post ID 插入他们的 Feed 表或缓存中(如 Redis List)
优点:
- 用户读 Feed 时非常快(直接读自己的 Feed)
- 更适合移动端、延迟要求高的场景
缺点:
- 高写放大(明星用户发 Post,系统要写入百万条记录)
- 极端用户(明星)影响系统性能
🧠 混合模式(Hybrid Model)✅ 推荐使用
核心思想:
- 对于普通用户,使用推模式
- 对于粉丝数巨大的用户(如 >10w),使用拉模式
实现方式:
- 用户发布 Post 时:
- 如果是普通用户:将 Post ID 推送到所有粉丝的 Feed(可以用 Redis List 或数据库表)
- 如果是大 V 用户:不推送,而是记录下来
- 用户拉取 Feed 时:
- 读取推送到 Feed 的 Post ID(推模式)
- 再加上拉取一些关注的大 V 的最新 Post(拉模式)
- 合并排序展示
示意图:
+------------+ +--------------+ +-----------------+
| 大V用户 |-----> | Post表 |<-----+ | 普通用户A的Feed |
+------------+ +--------------+ | +-----------------+
|
+------------+ +--------------+ |
| 普通用户B |--------> | Fan Feed |<-----+
+------------+ +--------------+
🛠️ 技术实现细节
1. Redis 存储用户 Feed
- 使用 Redis List 存储每个用户的 Feed(Post IDs)
- Key:
feed:{user_id}
- List 中是按时间排序的 Post ID
- 使用
LPUSH
插入,LRANGE
分页读取
2. 发布 Post 时推送
- 查询该用户的粉丝列表(可分页、异步)
- 将 Post ID 批量推送到粉丝的 Redis Feed 中
- 对于粉丝数 > 阈值(如10w)的用户,不推送,只记录
3. Feed 拉取逻辑
- 读取 Redis 中用户的 Feed(推送进来的 Post ID)
- 同时查询被关注的大 V 的最近 Post(拉模式)
- 合并后按时间排序返回前 N 条
4. 异步推送(重要)
- 使用消息队列(如 Kafka/NATS)异步推送 Post ID 到粉丝 Feed,避免同步写入压力
📌 数据一致性和过期策略
数据一致性
- Redis Feed 中只保存 Post ID,读取时再去数据库取详情(避免冗余)
- 如果 Post 被删除,读取时做校验
Redis 过期策略
- 每个用户的 Feed 只保留最近 N 条(如500条),用
LTRIM
控制长度 - 老数据由用户拉取历史时再实时查询数据库
🧪 示例流程
用户发 Post 流程(混合推送)
- 写入 Post 表
- 发送消息到消息队列(包含 Post ID)
- 消费者从队列读取,查询粉丝列表:
- 普通用户:推送到 Redis 中
- 大V用户:不推送
用户打开 Feed 页面流程
- 读 Redis List
feed:{user_id}
,拿到最近 100 条 Post ID - 查询这些 Post 的详情
- 同时查询关注的大 V 用户最近的 Post(比如每人最近5条)
- 合并排序,分页展示
🏗️ 可扩展优化
- Feed 分库分表
- Post ID 采用雪花算法,方便排序
- 粉丝列表缓存到 Redis,减少 DB 查询
- 使用 Elasticsearch 或 Timeline 服务做智能 Feed(个性化推荐)
📈 总结
策略 | 优点 | 缺点 | 适用场景 |
---|---|---|---|
拉模式 | 写入简单,数据新鲜 | 读开销大,不适合大规模 | 大 V 发布内容 |
推模式 | 读快,体验好 | 写入压力大,不适合明星用户 | 普通用户 |
混合模式 ✅ | 平衡读写性能,最常用方案 | 实现稍复杂 | 推荐用于生产环境 |