跳到主要内容

社交信息流系统设计

社交信息流案例要支持什么?​

  • 功能: 发布含文本或媒体的短帖、关注用户、浏览关注对象的信息流并搜索内容;
  • 扩展: 转发、收藏,以及指标与分析;
  • 质量目标: 高可用、低延迟和可扩展,重点区分内容写入与大规模信息流读取;
  • 产品边界: 原文以 Twitter 和 280 字符举例,这里沿用教学名称,不把它当当前产品限制;

如何重算帖子和媒体容量?​

  • 假设: 总用户 10 亿,DAU 为 2 亿,每人每天发布 5 条,10% 附带一个平均 50 KB 媒体,记录按 100 B;
项目计算结果
新帖子2 亿 × 510 亿/日
平均发布10 亿 / 8640011574/s,约 12K/s
媒体数量10 亿 × 10%1 亿/日
文本与元数据10 亿 × 100 B100 GB/日
媒体新增1 亿 × 50 KB5 TB/日
十年逻辑容量5.1 TB × 365018.615 PB,约 19 PB
平均入口5.1 TB / 8640059 MB/s,约 60 MB/s
  • 原文修正: 汇总表的 1 亿 DAU 与正文及计算不符,此处统一采用正文的 2 亿;
  • 读流量缺口: 12K/s 只是发帖平均速率,尚未包含信息流刷新、搜索、扇出和媒体下载;
  • 字节假设: 100 B 只是一项演示均值,完整多语言帖子、索引和属性可能更大,应重新采样;

哪些数据构成社交关系和信息流?​

表关键字段责任
usersid、name、email、dob、createdAt用户资料
tweetsid、userID、type、content、createdAt帖子事实
followersfollowerID、followeeID有方向的关注关系
favoritesuserID、tweetID、createdAt收藏关系
feedsid、userID、updatedAt用户信息流元数据
feeds_tweetsfeedID、tweetID信息流中的候选帖子
  • 唯一性: 关注和收藏关系应有复合唯一约束,使重复请求不生成多份关系;
  • 事实与投影: 帖子是权威内容,feed 中可只存帖子 ID 与排序信息,避免复制所有正文;
  • 存储选择: 关系与帖子可按各自访问模式选择关系库或分布式存储,不必所有表共用一个物理库;

API 怎样表达发布、关注和信息流读取?​

postTweet(userID, content, mediaURL?, requestID?) → tweetID
follow(followerID, followeeID) → result
unfollow(followerID, followeeID) → result
getNewsfeed(userID, cursor?, limit?) → TweetPage
  • 身份: userID 与 followerID 必须由认证上下文验证,防止替别人发帖或关注;
  • 幂等: 重复发帖请求应返回同一结果,关注和取消关注按目标关系状态处理;
  • 分页补充: 原文返回全部 Tweet[],实际需要稳定游标,避免新帖插入导致翻页漏项或重复;
  • 媒体引用: 上传与发布分开,发帖时校验引用的媒体确实属于当前用户且已可用;

服务怎样围绕内容与信息流分工?​

  • 用户服务: 管理身份与关系数据,按设计可进一步拆分关注图;
  • 帖子服务: 负责发布、删除、收藏等内容操作,是帖子状态的权威入口;
  • 信息流服务: 负责候选生成、排序和读取优化;
  • 搜索服务: 建立文本索引并处理检索与热点查询;
  • 媒体服务: 上传和处理媒体,使用对象存储与 CDN,通用路径见 即时通信;
  • 通知与分析服务: 消费业务事件,分别承担设备通知和指标处理,避免阻塞发帖;

拉取模型如何在读取时生成信息流?​

用户刷新 → 获取关注对象 → 读取各对象近期帖子
→ 合并候选 → 排序 → 分页返回
  • Fan-out on read: 发布时只保存帖子,读取时再把关注对象的内容组合起来;
  • 收益: 减少预计算与无效写入,不为从不活跃的粉丝持续生成 feed;
  • 代价: 关注对象很多时读取放大和合并成本高,延迟更难稳定;
  • 优化: 可缓存候选或预先生成部分结果,但需要明确新内容进入视图的时效;

推送模型如何在发布时更新粉丝信息流?​

帖子提交 → 扇出任务 → 获取粉丝列表
→ 将 tweetID 写入各粉丝 feed
  • Fan-out on write: 把生成成本移到写侧,用户读取时只需访问已经准备好的 feed;
  • 收益: 普通用户的信息流读取更快,减少每次请求临时联接;
  • 代价: 粉丝数越大写放大越严重,一条名人帖子可能生成海量更新;
  • 语义: 这里的 push 是写入服务端 feed,不等于一定实时向每个手机推送;
  • 故障处理: 扇出任务需要幂等和进度记录,重试不能重复插入同一帖子,积压时可显示较旧视图;

混合模型怎样处理拥有海量粉丝的作者?​

  • 混合策略: 普通作者使用写时扇出,名人等大粉丝作者只保存源帖子,读取时再合并;
  • 决策依据: 依据粉丝数量、活跃比例和发布频率估算写放大,阈值应通过实际负载确定;
  • 读取路径: 合并预生成 feed 与名人近期内容,再去重并统一排序;
  • 代价: 同时维护两种候选来源,作者跨过阈值时要设计迁移以免漏帖或重复;

排序如何让候选内容更符合用户兴趣?​

  • 排序目标: 在候选集合中根据相关性、时间和互动信号决定展示顺序;
  • 教学公式: 原文用历史 EdgeRank 思想示意 分数 = 亲密度 × 内容权重 × 时间衰减,不是当前平台算法声明;
  • 亲密度: 用户与作者越常互动,可能越相关;
  • 内容权重: 不同互动或内容类型赋予不同重要性,需防止单纯追求互动量带来偏差;
  • 时间衰减: 旧内容的权重逐渐降低,使新内容有机会进入视图;
  • 复杂模型: 可以使用机器学习组合更多特征,但必须有质量、延迟和可解释的评估目标;

转发怎样关联原始帖子?​

  • 最小模型: 新建帖子记录,把 type 标为转发,并保存 originalTweetID 或原文示例中的 content 引用;
  • 独立事件: 转发有自己的作者与时间,原帖内容仍由原帖记录管理;
  • 扩展: 也可建立专门转发表,方便独立约束重复转发和查询关系;
  • 删除与权限: 原帖删除或变为不可见后,转发渲染必须重新检查,不能借冗余内容绕过原帖权限;

搜索怎样从帖子数据派生?​

  • 文本搜索: 把可搜索帖子异步索引到专用搜索引擎,原文示例为基于 Lucene 的 Elasticsearch;
  • 索引延迟: 发布成功不代表立即可搜索,应定义刷新时效,删除和权限变更也需要同步;

热点怎样从近期搜索数据生成?​

  • 热点计算: 统计最近 N 秒内搜索词、标签和主题频率,每 M 秒刷新候选热点;
  • 个性化: 可以把用户兴趣用于热点排序,但热点统计窗口与推荐权重应明确区分;

共同关注如何辅助用户推荐?​

  • 社交图: 用户为节点,关注为有向边,比较邻接集合可发现共同关注或潜在关系;
  • 候选筛选: 从图关系找候选,再结合互动、兴趣或反馈排序;
  • 工具选择: 图数据库如 Neo4j、ArangoDB 可辅助遍历,但简单共同邻居计算不必强制引入图数据库;
  • 概念边界: 单向关注不等于双向朋友关系,推荐规则需要明确比较入边还是出边;

缓存与分区怎样支持海量读取?​

  • 分区: 用户关系可按 userID,帖子可按作者或帖子键及时间组织,feed 按接收用户组织以适配读取;
  • 缓存: 热帖对象和 feed 页可采用有明确 TTL 的缓存,所谓前 20% 热帖应由真实访问分布验证;
  • 新鲜度: 允许 feed 排序短暂滞后,不应允许已删除或无权访问的内容长期泄露;
  • 媒体: 大对象由对象存储和 CDN 承担,API 只返回必要的元数据及受控引用;
  • 分析: 原文用 Kafka 收集事件、Spark 做规模化分析,业务和统计应有独立失败边界;

故障和热点怎样影响最终体验?​

  • 服务与入口: 多实例和健康路由承接节点故障,服务间超时、重试和熔断需有总体预算;
  • 数据与缓存: 副本提高读取容量,缓存失效时限制回源并发,避免热点帖子压垮数据库;
  • 通知: 重试、去重和设备平台分发采用 即时通信的离线通知流程,不能把代理能力等同于用户必然收到;
  • 媒体成本: 压缩与派生格式降低存储和传输,但要维护原件及派生文件的生命周期;
  • 验证: 覆盖名人发帖、扇出中断、重复事件、原帖删除、关注变化和游标翻页时的新帖插入;