Skip to content

社交信息流 架构模板

代表产品:Twitter / X、Instagram、微博、抖音首页 一句话定位:把海量的「别人发的内容」,按你关心的关系/兴趣排好序,塞进你那一屏「首页信息流」。


1. 一句话定位

一个社交信息流产品 = 一台「内容分发引擎」:无数人在不停地往里发东西,系统要在你刷新的那一刻,从茫茫内容里挑出**「跟你有关的、你最可能想看的」**那几十条,排好序送到你眼前。

架构上最反直觉的一点:这类系统的难点不在写、不在存,而在「读时如何为每个人组装出一份个性化的列表」。一条发文很便宜,但「让一亿个人各自看到自己该看的那一版首页」这件事,会把整套架构逼到极限。一切设计,都在回答一个问题:这份「个性化列表」,到底是发文时就提前算好,还是等用户刷新时现算?

2. 业务本质:它在解决什么问题

用户要的是**「打开 App,不用思考,就有源源不断、跟我有关、还挺好看的内容」**。它取代的是「自己一个个去翻关注对象的主页」这件麻烦事——系统帮你把所有关注/感兴趣的来源,揉成一条永远刷不到底的流

价值与钱从哪来:

  • 注意力即货币:用户停留越久、刷得越多,可插入的广告就越多;
  • 信息流广告:广告本身就是「一条伪装成内容的内容」,无缝混进流里按曝光/点击计费;
  • 生态绑定:关系链和内容沉淀越厚,用户越离不开。

关键事实:发文是一次「写」,但每个粉丝刷新都是一次「读」,而读远远多于写。 一条发文可能被读几百万次。这意味着系统的负载几乎全压在「读」这一侧,这一条决定了后面几乎所有架构取舍——尤其是那个核心难题:这份首页要不要提前算好。

3. 核心需求与约束

功能性需求(系统要能做什么):

  • [ ] 发布内容:发文字/图/视频,可能 @ 人、带话题。
  • [ ] 关注关系(社交图):谁关注了谁,构成一张巨大的关系网。
  • [ ] 首页信息流(Home Timeline):聚合「我关注的人/我可能喜欢的内容」并排序。
  • [ ] 个人主页流(User Timeline):某个人发过的所有内容,按时间倒序。
  • [ ] 互动:点赞、转发、评论(它们本身又会成为新的内容/信号)。
  • [ ] 排序:决定「先给你看哪条」——时间倒序,或算法推荐。
  • [ ] 通知:有人关注你、@ 你、赞了你。

非功能性需求 / 质量属性(这才是架构的主战场):

质量属性目标为什么对这类系统重要
刷新延迟(读时延)< 几百毫秒用户下拉刷新就盯着看,转圈超过 1 秒就嫌卡。读路径必须极快。
读吞吐(Feed QPS)极高读量是写量的成百上千倍,是整个系统最大的流量来源。
发文到可见的延迟秒级可接受你发完,粉丝几秒后刷到是 OK 的;不要求强实时。这是宝贵的「松弛空间」。
可用性99.9%+刷不出内容用户立刻就走。读路径尤其要高可用。
最终一致即可你少看到一条、或晚几秒看到,无伤大雅。几乎不需要强一致——这是巨大的设计自由度。

关键约束(不可逾越的边界):

  • 🔴 读远多于写,且读是个性化的。不能像静态网页那样「算一次、人人共享」,每个人的首页都不一样。
  • 🔴 「大 V 问题」:粉丝数分布极度不均。普通人几百粉,头部账号几千万粉。任何「发文时给每个粉丝都做点事」的方案,都会在大 V 身上爆炸。 这是本架构的头号拦路虎。
  • 🔴 社交图巨大且热点集中:关系查询(谁关注了谁)频繁,且大 V 的粉丝列表本身就是超级热点。
  • 时间线是「无限下拉」的,要支持高效的分页与历史回溯。

4. 架构全景图

                          用户(刷新首页 / 发一条内容)
                            │                    ▲
              发文 │ 写路径   │                    │  读路径 │ 刷首页
                            ▼                    │
┌──────────────────────────────────────────────────────────────────────┐
│  接入层  API 网关 / 边缘                                                  │
│  • 鉴权、限流   • 媒体上传直传对象存储   • 读写分流                          │
└──────────┬──────────────────────────────────────────────┬─────────────┘
           │ 写                                             │ 读
           ▼                                                ▼
┌────────────────────┐                          ┌────────────────────────┐
│  发文服务            │                          │  Feed 读取服务           │
│  • 落库(权威内容)   │                          │  • 取出我的时间线条目     │
│  • 投递扇出(fan-out)│                          │  • 回填内容正文(批量)   │
└────┬────────┬──────┘                          │  • 调排序,组装最终列表   │
     │        │                                 └──────┬──────────┬───────┘
     │        │ 查粉丝列表                              │          │
     │        ▼                                         │          ▼
     │  ┌──────────────┐    ┌───────────────────────┐   │   ┌────────────┐
     │  │ 社交图服务     │◀───│   时间线存储(每人一条) │◀──┘   │ 排序/推荐   │
     │  │ (关注关系)    │    │   预计算好的「收件箱」    │       │  打分服务   │
     │  └──────────────┘    │   内存级 KV / 宽列        │       └────────────┘
     │         ▲            └───────────▲───────────────┘
     │ 写扩散   │ 普通人:推              │ 大 V:读时拉取并合并
     └─────────┴───────────────────────┘


┌──────────────┐   ┌───────────────────┐   ┌──────────────────────────────┐
│ 内容存储      │   │ 媒体存储 + CDN      │   │ 通知服务                       │
│ (帖子正文)   │   │ (图/视频→边缘分发) │   │ (被关注/被@/被赞 → 推送)      │
└──────────────┘   └───────────────────┘   └──────────────────────────────┘

灵魂部件是中间那块**「时间线存储(每人一条预计算好的收件箱)」,以及它两侧的写扩散 / 读聚合**两条路。整套系统的核心矛盾就藏在这里:普通人走「推」(发文时写进粉丝收件箱),大 V 走「拉」(读时再合并),两者在 Feed 读取服务里汇合。 这就是「混合模型」。

5. 组件职责

  • 接入层 / API 网关:鉴权、限流,并把读和写两条流量分开(读路径要极致优化、可大量缓存;写路径要可靠落库)。媒体走单独通道直传对象存储,不占业务链路。为什么需要:读写在这类系统里是「两个世界」,在最外层就分流,后面才好各自优化。
  • 发文服务:接收发文,把内容写进权威存储,然后触发扇出(fan-out)——决定这条内容要不要、以及怎样推进粉丝的时间线。为什么需要:它是写路径的核心,也是「推 vs 拉」决策的执行者。
  • 社交图服务(关注关系):维护「谁关注谁」这张巨大的有向图,提供「某人的粉丝列表 / 某人的关注列表」查询。为什么需要:扇出要靠它拿粉丝列表;读时拉取要靠它拿「我关注了谁」。它是关系的唯一权威。
  • 时间线存储(每人一条预计算收件箱):为每个用户维护一条已经排好序的内容 ID 列表(注意:存的是引用/ID,不是正文)。刷新时直接读这条列表即可。为什么需要:把「读时现算每个人的首页」这件昂贵的事,提前在写时做掉——用空间换读时延,这是抗住海量读 QPS 的关键。
  • Feed 读取服务:用户刷新时,取出时间线条目,批量回填内容正文,调排序服务打分,组装成最终一屏返回。为什么需要:时间线里只有 ID,真正给用户的是带正文、带排序的完整列表,这层负责「组装」。
  • 排序 / 推荐打分服务:决定条目的先后——可以是纯时间倒序,也可以是按「你多大概率会互动」的算法打分。为什么需要:内容供给远大于用户能看的量,排序质量直接决定用户停留时长,也就是直接决定收入。
  • 内容存储(帖子正文):存帖子本体(文字、引用的媒体地址、作者、时间)。一次写、海量次读、基本不改。
  • 媒体存储 + CDN:图片/视频存对象存储,通过 CDN 边缘节点分发。为什么需要:媒体又大又被反复读,必须靠 CDN 把流量挡在源站之外、把延迟拉到用户身边(详见视频流模板)。
  • 通知服务:被关注、被 @、被赞时,异步生成通知并推送。为什么需要:互动反馈是社交粘性的核心,但它不能拖慢发文/读 Feed 的主链路,所以异步。

6. 关键数据流

场景一:普通用户发一条内容(写扩散 / 推模型)

1. 用户发文 ──▶ 网关 ──▶ 发文服务
2. 发文服务:把正文写入【内容存储】(这是权威副本)         ← 这一步永远要做
3. 发文服务 ──▶ 社交图服务:取出「我的粉丝列表」(比如 800 人)
4. 异步扇出:把这条「内容 ID」逐个写进 800 个粉丝的【时间线收件箱】
        粉丝A 收件箱: [新ID, 旧, 旧, ...]
        粉丝B 收件箱: [新ID, 旧, 旧, ...]
        ... (写 800 次)
5. 完成。粉丝下次刷新时,这条已经躺在他的收件箱里了。

这叫写扩散(fan-out on write):发文时多干活(写 800 份),把读时变得极简(直接读自己那一条)。对粉丝不多的普通人,这非常划算——因为读远多于写,在写侧多花一点,省下海量读侧的开销。

场景二:用户刷新首页(读路径)

1. 用户下拉刷新 ──▶ 网关 ──▶ Feed 读取服务
2. 读取服务:取出我的【时间线收件箱】最新 N 条 ID(普通人的内容已预存于此)
3. 【混合关键】再去查我关注的「大 V」们最近发了什么 ── 实时拉取并合并进来
        (大 V 的内容没有被推给我,要在这一刻现拉)
4. 批量回填:拿这批 ID 去【内容存储】取正文 + 去媒体/CDN 取图
5. ──▶ 排序服务:对这批候选打分排序(时间序 或 算法推荐)
6. 组装成一屏 ──▶ 返回用户

注意第 2、3 步:普通人的内容是「提前推好、直接读」,大 V 的内容是「读时现拉、临时合并」。 一次刷新同时用到了「推」和「拉」——这正是混合模型的精髓。

场景三:大 V 发一条内容(为什么不能写扩散)

某大 V(5000 万粉丝)发一条文 ──▶ 发文服务
   若按「推模型」扇出 ──▶ 要往 5000 万个收件箱各写一次!!
        → 一条发文 = 5000 万次写,瞬间打爆存储和队列(这就是「扇出爆炸」)
   ✅ 混合方案:大 V 不扇出,只写自己的【个人主页时间线】
        → 由粉丝在「刷新读取」时(场景二第 3 步)主动来拉、来合并

这是本架构最重要的一张图:「大 V 问题」用「推拉分治」破解——普通人推、大 V 拉。 把「会爆炸的那一小撮」单独切出来走拉模型,其余绝大多数走推模型。

7. 数据模型与存储选择

核心实体:用户 ─(关注)─▶ 用户(构成社交图);用户 ──发布──▶ 帖子(post);帖子 ──产生──▶ 互动(赞/转/评);用户 ── 拥有 ──▶ 时间线(timeline,一串帖子 ID)

数据存储类型为什么
用户账户 / 资料关系型要事务、强一致(账号、安全相关)
关注关系(社交图)图 / 宽列(邻接表)频繁查「某人的粉丝/关注列表」,本质是图的邻接关系;粉丝列表可能极长
帖子正文文档型 / KV(按 ID 取)一次写、海量读、基本不改,按帖子 ID 直接取最快
时间线收件箱(每人一条 ID 列表)内存级 KV / 宽列读路径核心,要极低延迟、按用户 ID 直读;只存 ID 不存正文,省空间
媒体(图/视频)对象存储 + CDN文件大、不可变、被反复读,必须边缘分发
互动计数(赞数/转发数)内存级计数器 / KV写极频繁、读极频繁,要高速增减,可接受最终一致
排序所需特征 / 信号列存 / 特征存储海量行为日志,供排序模型离线/近线计算

教学点:时间线收件箱里只存「内容 ID」,不存正文。 一条爆款被几百万人的收件箱引用,若每条都存一份正文,空间瞬间爆炸;存 ID、读时再批量回填正文,是「引用 vs 拷贝」的经典权衡。用「内存级 KV」描述时间线存储,是因为它的访问形态是「按用户 ID 极低延迟读一条有序列表」,而不是因为某个具体产品。

8. 关键架构决策与权衡 ⭐

决策 1:Feed 用推模型(写扩散)、拉模型(读扩散)还是混合?(本架构的命门)

  • 拉模型(fan-out on read):发文时只存自己的;用户刷新时,现去查「我关注的所有人」最近发了啥,当场聚合排序
    • 优点:写极轻(只写一份),没有任何扇出,实现简单,改关注关系立即生效。
    • 代价:读极重。你关注 2000 人,每次刷新就要去聚合 2000 个人的最新内容——读放大严重,刷新延迟高,而读又是最高频操作,扛不住高 QPS。
  • 推模型(fan-out on write):发文时就把内容 ID 推进所有粉丝的收件箱;用户刷新时直接读自己那一条,极快。
    • 优点:读极轻(直读一条预排好的列表),完美匹配「读远多于写」。
    • 代价:写会扇出;遇到大 V(千万粉丝)直接扇出爆炸——一条发文要写千万次,打爆写入与存储。
  • 混合(Hybrid):绝大多数普通用户走推(粉丝少,扇出便宜,享受读极快);少数大 V 走拉(粉丝太多,不推,改由粉丝读时来合并)。
  • 取向:规模化后必然走混合。因为读远多于写 → 默认想用推;但大 V 让纯推爆炸 → 把大 V 切出来走拉。「对 99% 便宜的情况做优化、把 1% 会爆炸的情况单独处理」,是这类倾斜分布系统的通用解法。代价是系统里同时存在两条路径,读取服务要做「推来的 + 拉来的」合并,复杂度上升。

决策 2:「大 V 写扩散爆炸」具体怎么破?

  • 问题:粉丝数是极度长尾的——绝大多数人粉丝很少,极少数人粉丝上千万。一条大 V 发文若推给所有粉丝,瞬时产生千万级写操作,撑爆队列和存储,还拖慢大 V 的发文响应。
  • 破解:给账号设一个「粉丝数阈值」。低于阈值=普通人=发文时正常扇出(推);高于阈值=大 V=不扇出,内容只留在自己的个人主页时间线里,由粉丝在读取时主动拉取并合并进首页。
  • 取向:这是混合模型的落地手段。代价:① 读取服务变复杂(要识别「我关注了哪些大 V」并实时拉取合并);② 阈值要调,且存在「中 V」灰度地带;③ 大 V 内容的读路径更重,需对其内容做额外缓存(因为会被海量粉丝同时来拉,是天然热点)。本质是把「写时爆炸」转移成「读时多一次合并」,而读时我们有缓存可用。

决策 3:时间线(收件箱)存在哪、存多深?

  • 全存进慢速持久库:省内存,但读延迟高,违背「读要极快」。
  • 全量、永久地放进高速存储:读极快,但海量用户 × 长历史,内存成本爆炸。
  • 取向:时间线收件箱放内存级 KV/宽列以保证读速度,但只保留最近的有限条数(比如最近几百条)。用户向下翻很旧的内容时,再走「降级路径」从持久存储慢查。因为「绝大多数刷新只看最新的一屏」,为冷门的深翻保留全量高速副本不划算。 又是「为热路径优化、冷路径降级」。

决策 4:排序——纯时间倒序,还是算法推荐?

  • 时间倒序:最新的在最上面。简单、可预测、所见即关注。但内容多了会淹没好内容,你关注的「话痨」会刷屏。
  • 算法推荐:按「你多大概率会停留/互动」打分排序,可掺入「你没关注但可能喜欢」的内容。
    • 优点:停留时长↑、收入↑、能突破关系链推好内容。
    • 代价:需要一整套行为信号采集 + 特征 + 打分模型的近线/在线管线,算力开销大;还带来「信息茧房 / 不可解释」等产品与伦理问题。
  • 取向:早期用时间序(简单、够用);成熟期普遍转向算法排序,因为它直接驱动核心商业指标。架构上这意味着读取服务后面要挂一套排序子系统,且排序所需的特征要被持续计算和缓存。关键:排序是在「读取时对一小批候选(几百条)打分」,而不是「为所有人实时重算全网」——后者是算力上做不到的误区。

决策 5:发文到可见,要多实时?

  • 追求「发完粉丝立刻可见」:扇出必须同步完成,拖慢发文响应,放大爆炸问题。
  • 接受「秒级延迟」:扇出异步化(进队列慢慢写),发文服务快速返回。
  • 取向:几乎一定接受秒级最终一致。社交流不是金融交易,晚几秒刷到完全可接受。这份「松弛」是这类系统最宝贵的设计自由度——它让扇出可以异步、可以削峰、可以失败重试,而不必卡住用户。

9. 规模化与瓶颈

和普通系统不同:这里的瓶颈集中在「读侧的扇出/聚合」和「大 V 热点」,而不是单纯的数据库容量。

  • 第一个瓶颈:Feed 读 QPS 巨大。 读是写的成百上千倍,每次刷新都要组装个性化列表。 破解:① 预计算时间线(推模型,把读时现算变成读时直取);② 多级缓存——热门内容正文、热门用户的时间线、整屏渲染结果都可缓存;③ 时间线只存 ID + 批量回填,减小数据量。
  • 第二个瓶颈:大 V 扇出爆炸。 一条发文要触达千万收件箱。 破解:推拉混合——大 V 不扇出,改读时拉取合并(决策 1、2)。这是绕不开的核心手段。
  • 第三个瓶颈:社交图查询与热点。 大 V 的粉丝列表本身是超级热点,频繁被扇出/拉取读取。 破解:① 邻接表分片;② 缓存超大粉丝列表;③ 对超级热点账号的内容单独做热点缓存层(因为它会被海量粉丝同时来拉)。
  • 第四个瓶颈:排序算力。 算法推荐要为每次刷新的候选打分。 破解:① 特征离线/近线预计算好,在线只做轻量打分;② 候选集先粗排召回缩到几百条,再精排;③ 排序结果短时缓存。
  • 媒体带宽:图/视频流量巨大 → CDN 边缘分发 + 分层缓存(详见视频流模板)。

10. 安全与合规要点

  • 内容审核(头号风险):这类系统是违法/有害/虚假信息的天然温床。发布链路要接审核(自动 + 人工),先审后扩散或边扩散边审,违规内容要能快速从所有收件箱召回
  • 隐私与可见性:私密账号、屏蔽/拉黑、谁能看到我的内容——这些可见性规则必须在「扇出」和「读取」两侧都强制执行,不能只在前端隐藏。一个常见漏洞是:内容已经推进了不该看到它的人的收件箱。
  • 滥用与垃圾:机器人刷粉、刷量、批量发垃圾内容。需要限流、行为风控、虚假账号识别。
  • 关系链即敏感数据:「谁关注谁、谁给谁点赞」会暴露社会关系,要做访问控制与最小暴露。
  • 排序与操纵:推荐系统可能被刷量操纵、或被用于不当信息推送,需要可观测与人工兜底。

11. 常见误区 / 反模式

  • 纯推模型,不管大 V → ✅ 一遇到大 V(千万粉)就扇出爆炸。必须对大 V 走拉模型(推拉混合)。
  • 纯拉模型,扛高读 QPS → ✅ 每次刷新都聚合上千个关注对象,读放大严重,刷新慢。普通用户应走预计算的推模型。
  • 每次刷新都为所有人实时重算全网排序 → ✅ 算力上做不到。要预计算时间线 + 只对一小批候选打分
  • 时间线收件箱里存正文 → ✅ 一条爆款被几百万收件箱引用就空间爆炸。应只存内容 ID,读时批量回填正文
  • 扇出同步完成、卡住发文响应 → ✅ 扇出应异步,发文快速返回;社交流接受秒级最终一致。
  • 可见性/拉黑只在前端隐藏 → ✅ 必须在扇出与读取两侧强制执行,否则内容会泄漏进不该看到的人的收件箱。
  • 媒体从源站直发 → ✅ 图/视频必须走 CDN 边缘分发,否则带宽和延迟都崩。

12. 演进路线:MVP → 成长期 → 成熟期

阶段用户/规模量级架构长什么样此时该操心什么
MVP验证想法 / 几万用户拉模型:发文只存自己的,刷新时现查关注对象聚合排序;单一数据库;时间倒序;媒体先简单直发或小 CDN先验证有没有人发、有没有人看。拉模型实现最简单,粉丝都不多时读放大还不致命
成长期百万级引入推模型 + 预计算时间线抗读 QPS;加多级缓存;媒体上 CDN;扇出异步化;开始出现大 V 苗头找到读侧瓶颈,把刷新延迟压下来;盯住第一批大 V,准备推拉混合
成熟期千万~亿级推拉混合(普通人推、大 V 拉)+ 算法推荐排序(召回粗排精排)+ 多级/热点缓存 + 全球多区域 + 完整内容审核与风控大 V 热点、排序算力与质量、成本、容灾、内容合规与信息安全

13. 可复用要点

  • 💡 读写不对称时,把成本搬到「次数少」的那一侧。 读远多于写 → 在写时多做(扇出、预计算),让读极简。这是「写扩散」背后的通用思想,在任何「写少读多」系统都适用。
  • 💡 倾斜分布(长尾)要分而治之:为大多数的便宜情况优化,把少数会爆炸的情况单独处理。 普通人推、大 V 拉,就是这条智慧的化身;它同样适用于热点商品、热点房间、超级用户等一切「一小撮极端值」场景。
  • 💡 存「引用」而不是存「拷贝」。 时间线只存内容 ID、读时回填——避免一份热门内容被复制百万份。这是去重、省空间的通用招式。
  • 💡 预计算是用空间换读时延的利器,但只为热路径预计算、冷路径降级。 时间线只保留最近 N 条、深翻才慢查,是「别为冷门付全量代价」。
  • 💡 找到系统的「松弛维度」并把它用足。 这里是「发文到可见可以慢几秒」的最终一致——正是它让扇出可以异步、可削峰、可重试。识别出哪里允许「不那么实时/不那么强一致」,往往就解锁了整套架构的弹性。

🎯 随堂检验

🤔一个有千万粉丝的超级大 V 发帖,最合适的扩散方式是?
  • A写时推送给所有粉丝(写扩散)
  • B读时拉取 / 推拉混合,避免一次推爆
  • C和普通用户完全一样处理

参考原型与延伸阅读

本模板基于以下工程剖析真实开源项目整理。

📖 工程博客:

🔧 开源原型(可直接读代码):

  • mastodon/mastodon — 开源微博社交网络,可读源码的关注图与主页时间线投递实现。

📌 一句话记住社交信息流:它不是「存帖子的数据库」,而是「一台为每个人现场组装个性化列表的分发引擎」——核心矛盾永远是『这份首页提前算好(推),还是刷新时现算(拉)』,而答案对普通人和大 V 截然不同。