最近开发遇到一个场景,记录一下。 场景:用户可以举报别人或者某个评论,管理员处理举报,系统将处理的反馈发送给所有举报者。 这个,仔细一想,不就是经典的社交平台信息流(Feed)系统设计问题吗?尤其是当涉及到「高影响力用户」(如被几百万人关注的用户)发布内容时,系统需要高效地将该内容分发到大量用户的 Feed 中,确保性能和延迟可控。

整个场景,可以简化为如下:

  1. 系统有用户user;
  2. 用户间可以相互关注;
  3. 用户可以发布 Post;
  4. 用户在信息流中,可以看到自己关注的人发布的Post;
  5. 场景是,如果一个用户被很多人关注了(比如几百万人),这个用户发布一个 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 流程(混合推送)

  1. 写入 Post 表
  2. 发送消息到消息队列(包含 Post ID)
  3. 消费者从队列读取,查询粉丝列表:
    • 普通用户:推送到 Redis 中
    • 大V用户:不推送

用户打开 Feed 页面流程

  1. 读 Redis List feed:{user_id},拿到最近 100 条 Post ID
  2. 查询这些 Post 的详情
  3. 同时查询关注的大 V 用户最近的 Post(比如每人最近5条)
  4. 合并排序,分页展示

🏗️ 可扩展优化

  • Feed 分库分表
  • Post ID 采用雪花算法,方便排序
  • 粉丝列表缓存到 Redis,减少 DB 查询
  • 使用 Elasticsearch 或 Timeline 服务做智能 Feed(个性化推荐)

📈 总结

策略优点缺点适用场景
拉模式写入简单,数据新鲜读开销大,不适合大规模大 V 发布内容
推模式读快,体验好写入压力大,不适合明星用户普通用户
混合模式 ✅平衡读写性能,最常用方案实现稍复杂推荐用于生产环境