优步与FB工程师拆解Feed架构 纯拉取模型仅有0.2%利用率遭弃

[Chinese] Mock Interview: design facebook feeds — Scott Shi

Scott Shi

超大规模动态流必须在开局采用推拉混合模型,通过时间衰减打分与布隆过滤器过滤大V稀疏已读,否则跨分区读放大与千倍冗余查询将直接摧毁低延迟SLA。

AI-assisted summary of [Chinese] Mock Interview: design facebook feeds — original source. Check the original for full context.

Source published:

Key ideas

  • Facebook News Feed 等超大规模社交系统必须采用推拉结合的混合模型(Hybrid Model),对普通用户发帖进行写扩散(Fan-out),对大 V 与公共主页则跳过推送改为读时拉取,以平衡延迟与读写放大。 (22:30)
  • 大厂 Feed 流系统的性能 SLA 极度倾斜于延迟而非可用性,Instagram 允许 99.9% 可用性,但 Feed 接口发生 0.2% 至 0.3% 的延迟退化即难以合入上线,超过 1.5% 则完全阻断上线。 (70:00)
  • Facebook 底层将所有帖子、点赞与用户行为抽象为以 TAO 为前置缓存、UDB(MySQL)/ ZippyDB 为底座的异构图(Heterogeneous Graph)模型,并通过 Ads Finder 对 Indexer 的并发扇出和丢弃超时机制实现广告毫秒级渲染。 (65:00)
  • 动态信息流防重复与重排可以通过时间衰减算法(Exponential Decay)避免全量计算,结合以周为单位滚动的布隆过滤器(Bloom Filter)在约 8GB 单机内存下高效完成稀疏动态与大 V 帖子的去重。 (80:00)

Detailed notes

## Background 该视频由 YouTube 频道 Scott Shi 于 2021 年 1 月 5 日发布。视频内容为一场针对“设计 Facebook News Feed”的系统设计模拟面试(Mock Interview),简介中附有配套的 Google Doc 笔记链接以供参考。

## TL;DR 视频记录了一场关于 Facebook 动态流(News Feed)的深度系统设计面试与复盘。双方从百亿读写容量估算、对象存储选型切入,深入推导了纯拉取(Pull)模型带来的读放大危机,并论证了普通用户 Fan-out 与大 V 用户拉取的推拉结合(Hybrid)架构。前 Facebook 资深工程师林世耀作为嘉宾参与讨论,剖析了 Facebook 内部高度并行的 Ads Finder/Indexer 广告召回体系、TAO 异构图数据模型、极度严苛的毫秒级延迟 SLA 以及针对动态 Feed 的时间衰减与布隆过滤器去重方案。

## Atmosphere & style 整场交流专业硬核且节奏紧凑,面试官风格严厉敏锐、一针见血,在数据利用率和选型假设上不断进行极限施压,甚至直言纠偏候选人的表述策略。随着中场资深大厂嘉宾的加入,气氛由单纯的面试交锋升华成了充满工业界内幕与工程黑魔法的高阶技术研讨会,既有硅谷工程师探讨分布式架构与 SLA 边界时的理性严谨,又穿插着轻松通透的职场职级与复盘交流。

## Bulletin - [22:30](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1350s) Facebook News Feed 等超大规模社交系统必须采用推拉结合的混合模型(Hybrid Model),对普通用户发帖进行写扩散(Fan-out),对大 V 与公共主页则跳过推送改为读时拉取,以平衡延迟与读写放大。 - [01:10:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=4200s) 大厂 Feed 流系统的性能 SLA 极度倾斜于延迟而非可用性,Instagram 允许 99.9% 可用性,但 Feed 接口发生 0.2% 至 0.3% 的延迟退化即难以合入上线,超过 1.5% 则完全阻断上线。 - [01:05:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=3900s) Facebook 底层将所有帖子、点赞与用户行为抽象为以 TAO 为前置缓存、UDB(MySQL)/ ZippyDB 为底座的异构图(Heterogeneous Graph)模型,并通过 Ads Finder 对 Indexer 的并发扇出和丢弃超时机制实现广告毫秒级渲染。 - [01:20:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=4800s) 动态信息流防重复与重排可以通过时间衰减算法(Exponential Decay)避免全量计算,结合以周为单位滚动的布隆过滤器(Bloom Filter)在约 8GB 单机内存下高效完成稀疏动态与大 V 帖子的去重。

## Key takeaways - [29:50](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1790s) 系统设计面试应开门见山提出混合架构(Hybrid Solution),避免从纯粹规范化拉取模型(Pull Model)起步,否则低至 0.2% 的数据利用率和跨分片读放大极易在面试官心中留下负面感知。 - [20:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1200s) 纯拉取模型(Pull Model)在 500 个好友场景下,为返回 50 条动态需扫描 25,000 条帖子,读放大和跨分片分散-聚合(Scatter-Gather)调用无法满足 P99 10ms 级别的延迟 SLA。 - [05:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=300s) 用户发布动态与刷 Feed 的 QPS 呈现典型读多写少特征(10 亿 DAU 下写入峰值约 5 万至 10 万 QPS,而读取峰值可达数十万至百万量级),多媒体文件必须剥离到对象存储(如 S3)并结合 CDN/缓存加速,不可选用面向高吞吐牺牲延迟的 HDFS。 - [35:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=2100s) 时间线(Timeline)缓存常采用 Redis Sorted Set(ZSET)结构(Member 为 PostID,Score 为时间戳),但设计分页时不可随意假设 PostID 具备类似 Twitter Snowflake 的时间递增语义,需将物理主键与时间范围解耦。 - [50:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=3000s) 去重方案需依数据密度区分:普通好友的密集时间线流只需维护已读游标(Read Offset)或标记,而大 V 动态及广告与全量用户的交互属于典型的稀疏矩阵(Sparse Map),更适合引入布隆过滤器过滤。 - [45:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=2700s) 布隆过滤器应用于信息流去重时,应采用滚动时间窗口(如保留本周、上周与两周前三个实例)来定期剔除过期数据,避免内存无限膨胀。 - [55:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=3300s) Facebook 内部广告系统采用 Ads Finder 与下游多个 Ads Indexer 的高并发 Fan-out 检索架构,通过严格的超时丢弃(Timeout Drop)机制保障整条信息流的极低延迟预算。 - [01:00:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=3600s) 广告投放是基于用户画像特征谓词(Targeting Predicates)的倒排匹配,Feed 内容渲染与广告插入是两条独立的并发管线,最终在网关或聚合层合并输出。 - [01:05:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=3900s) Facebook 的核心数据哲学是异构图(Heterogeneous Graph),TAO 充当感知缓存层,底层强一致性依赖 MySQL(UDB),弱一致性与写后读依赖基于 RocksDB 的分布式 KV 存储 ZippyDB。 - [01:10:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=4200s) 移动端体验优化大量依赖乐观 UI 更新(Optimistic UI)、客户端预拉取(Prefetch)以及内联 Base64 模糊缩略图占位渲染,跨设备即时一致性在社交产品中普遍妥协为最终一致性。 - [01:15:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=4500s) 系统设计标准推导流程应当是先明确需求指标与读写比,推导出宏观架构与数据库选型,最后结合所选存储特性针对性设计 Primary Key 与 Partition Key,而非一上来就盲目设计 Schema。 - [01:25:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=5100s) Feed 排序打分不应在每次刷新时做全量重排,而是结合初始基础分并在客户端刷新时执行指数时间衰减(Exponential Decay)折算,配合未读过滤保障前排内容的新鲜度与吸引力。

## Detailed walkthrough ### [00:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=0s) 背景介绍与 Facebook News Feed 需求对齐

面试官首先做了一个自我介绍:他目前在 Uber 的 Data Infra 团队工作了两年多,此前在微软工作了一年半,在 Uber 内部主要参与 Apache Flink 以及与 Athena 相关的项目。他提到,去年 11 月他曾加入 Uber 的一个 strike team(当时签署了保密协议不能透露细节),团队当时设计了一套 graph/relationship 系统,完成后他才读到 Facebook TAO 的相关论文,惊讶地发现两者底层思想高度重合。他特别推荐大家阅读 Facebook TAO(The Associations and Objects)这篇经典论文,指出它不仅是 Facebook Feeds 的核心基石,也启发了业内许多系统(例如 Uber 的 Schemless)。

随后两人切入面试正题:设计 Facebook News Feed。面试官坦言自己平时基本不用 Facebook,早在 2013、2014 年就不再使用了,因为不喜欢把个人数据公开给平台;今天题目中的需求描述借用了 Grokking the System Design Interview 的框架,尽管该教程对核心挑战的解答并不完善,但需求定义清晰: - 用户信息流(News Feed)由关注的好友、群组(Groups)以及公共主页(Pages)发表的内容聚合生成。 - 动态内容形式丰富,包括纯文本、图片、视频等。 - 系统必须支持用户发布新内容(Post),并追加到对应受众的 News Feed 中。 - 支持签到与位置标记(Check-in,例如去金门大桥发布状态),并支持插入 Facebook 最核心的营收来源——广告(Ads)。

双方针对核心规模数据达成初步共识: - 社交关系密度:假设每个用户平均拥有约 500 个好友(远高于微信等密友社交)。 - 月活跃用户(MAU):总用户规模约 25 亿至 27 亿。 - 日活跃用户(DAU):假定为 10 亿(1 Billion)。

---

[05:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=300s) 容量估算与多媒体存储选型争议(HDFS vs. S3)

候选人开始围绕核心功能梳理容量与吞吐量估算。系统核心用例为:发帖(Post)、刷 Feed(Get Feed)、评论(Comment)和点赞(Like)。 假设 10 亿 DAU 平均每天每人发布 1 条帖子,每日发帖总量为 10 亿条。针对文本帖子,单条文本按 1 KB 估算,一天仅文本存储增量就达到 1 TB($10^9 \times 1\text{ KB} = 1\text{ TB}$)。

关于数据保留时长,候选人提出按 5 年甚至 1 年滚动采购存储进行估算,认为旧系统在长期演进中会被淘汰。面试官立刻纠正并明确产品原则:“We don't want to lose data. Data is everything in Facebook.” 用户直到今天仍能查看自己 10 年前的历史状态,系统必须支持永久存储;底层架构应做到动态水平扩展(horizontally scalable),按年扩容是运维层面的事,架构设计必须保证持久保留。

在多媒体帖子(图片与视频)的预估上,候选人提到图片单张可能达到 1 MB,视频更大。候选人提出所有多媒体文件可以统一存放在 HDFS 中,称其“灵活且可扩展”。这一提议立即遭到了面试官的反驳: - 面试官指出 HDFS 的设计初衷是针对吞吐量(throughput)进行吞吐优先优化,牺牲了访问延迟(latency)。对于面向终端用户的在线高并发产品,原生 HDFS 的单次读调用甚至可能超过 1 分钟,根本无法满足 Feed 流秒级呈现的要求。 - 候选人尝试补充解释:可以使用 HBase 构建在 HDFS 之上,将 HDFS 作为底座,上层由 HBase 承担在线检索。 - 面试官建议在系统设计面试中不必重复造轮子去自建分布式大对象存储,直接选用云厂商成熟开箱即用的大对象存储服务(如 AWS S3),并在其上层架设缓存层(Cache/CDN),才是面向现代低延迟高并发读取的合理做法。

---

[10:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=600s) 读写 QPS 预估与系统架构方向展开

完成了存储估算后,候选人推导系统的并发吞吐量: - **写 QPS(发帖)**:一天 10 亿条发帖,以每天约 $100,000$ 秒计算(实际为 86,400 秒),平均写入 QPS 约为 10,000;考虑日常峰值(按 5 到 10 倍峰值比估算),写入峰值 QPS 约为 50,000 至 100,000。 - **读 QPS(刷 Feed)**:读操作通常远高于写操作。假设 10 亿日活用户每天平均刷新 App 或网页 10 次,每日 Feed 刷新请求高达 100 亿次,平均读 QPS 约为 100,000,峰值读 QPS 轻易达到数十万甚至百万量级。这是一个典型的重读轻写(read-heavy)系统。

候选人准备转向 API 设计,但面试官直接打断并强调核心重点:“Let's go to architecture. I'm really interested in architecture here. I want to see how to handle architecture.” 面试官希望深入剖析其服务解耦、拓扑流转与数据合并机制。

- 每日活跃用户(DAU):10 亿 - 每日发帖总量:10 亿条 - 平均发帖 QPS:~10,000;峰值发帖 QPS:50,000 ~ 100,000 - 每日刷 Feed 次数:100 亿次 - 平均读 Feed QPS:~100,000;峰值读 QPS:500,000+

---

[15:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=900s) 初版微服务架构划分与 Pull 模式流程推演

候选人开始绘制系统架构拓扑图。他将整个在线链路拆分为用户发帖与用户读 Feed 两条主干线: 1. **写流程(Post Pipeline)**:客户端调用 Post Service,服务将帖子元数据(发布者 ID、内容、创建时间戳等)写入 Post Storage,而图片、视频等多媒体文件剥离存入专门的 Media Storage(如前述的对象存储)。 2. **读流程(Feed Pipeline)**:客户端向 Feed Service 发起拉取请求。为了组装用户关注者的动态,Feed Service 必须与 Relation Service(关系服务)交互,由其查询出该用户关注的所有好友与主页列表(Followees)。 3. **数据聚合与多路归并(Fan-out-on-read / Pull Model)**:Feed Service 拿到所有的 Followee IDs 之后,再批量向 Post Storage 请求这些关注者近期发布的帖子列表。由于每位被关注者的发帖在底层存储中均按时间戳或自增 Post ID 建立了索引,Feed Service 可以直接使用归并排序(k-way merge),将来自多个好友的有序动态合并成一条全局按时间排序的时间线(Timeline),最后回传给客户端。

```mermaid flowchart TD accTitle: 初版 Pull 模式 Feed 架构 accDescr: 描述用户发帖写入存储,以及客户端向 Feed Service 发起多路拉取并归并的架构流程

subgraph UserInteraction ["客户端交互"] User["用户 (Client)"] end

subgraph WritePath ["写流程 (Post Path)"] PostSvc["Post Service"] PostDB[("Post Storage\n(元数据/文本)")] MediaStore[("Media Storage\n(S3/对象存储)")] end

subgraph ReadPath ["读流程 (Feed Pull Path)"] FeedSvc["Feed Service"] RelSvc["Relation Service\n(好友/关注关系)"] end

User -->|"1. 发布帖子"| PostSvc PostSvc -->|"2a. 保存元数据"| PostDB PostSvc -->|"2b. 存储大文件"| MediaStore

User -->|"A. 请求刷 Feed"| FeedSvc FeedSvc -->|"B. 查询所有关注对象 (Followees)"| RelSvc FeedSvc -->|"C. 批量拉取好友帖子"| PostDB FeedSvc -.->|"D. 多路归并 (Merge Sort) 并返回"| User ```

这种基础方案本质上是典型的读扩散(Pull 模式)。此时整个读放大风险尚未完全爆发,候选人刚勾勒出从 Relation Service 拿名单到 Post Storage 多路归并的雏形,为后续关于 Fan-out 模式权衡与缓存层深化的讨论埋下了铺垫。

[20:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1200s) 规范化拉取模型的缺陷与读放大危机

讨论首先聚焦于最基础的“规范化模型”(Normalized Model / Pull Model)。候选人解释道,在该模式下,当某位用户想要获取信息流(Feeds)时,系统会查询该用户关注的所有对象(Followees),遍历拉取他们发布的所有帖子(Posts),最后在内存中进行聚合与按时间排序并合并(Merge)。面试官随即抛出具体数字进行量化推演:“在 Facebook 这种场景下,你每次打算为用户拉取多少条动态?”候选人提出一页大概 10 条,乘以 5 页左右,即单次获取 50 条帖子。

面试官紧接着追问:“如果你有 500 个好友,为了拿到这 50 条帖子,你需要从每个好友那里查多少条?”候选人回答为防止某些好友没发动态,至少每位好友得查 50 条。面试官直接点出其中悬殊的成本: - 500 位好友 $\times$ 50 条动态 = 25,000 条帖子被读取。 - 最终却仅仅向客户端返回 50 条。

除了低下的数据利用率,面试官着重指出了延迟优化与架构瓶颈(Read Amplification)。即便采用 NoSQL 无需分片,或者在 MySQL 等关系型数据库的应用层做 Sharding,当用户单次拉取需要发起 500 次跨数据库的分区调用时,整体耗时就很难维持在单次读取 P99 约 10 毫秒的理想水平。面对如此巨大的读放大,面试官直言:“我非常怀疑这种设计能否支撑高并发的 Scale。”

```mermaid flowchart TD accTitle: Pull Model Read Amplification accDescr: Diagram illustrating massive read amplification in pure pull feed model

User["User Client"] -->|"1. Request feed (need 50 posts)"| FeedSvc["Feed Service"] FeedSvc -->|"2. Query 500 followees"| RelDB[("Relationship DB")] FeedSvc -->|"3. Scatter-gather 50 posts each"| PostsDB[("Posts Shards / Partitioned DB")] PostsDB -->|"25,000 posts scanned"| FeedSvc FeedSvc -->|"4. In-memory sort & merge"| MergeEngine["Merge & Truncate"] MergeEngine -->|"Return only 50 posts"| User ```

---

[22:30](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1350s) 引入 Fan-out 推送模型与名人处理(Hybrid Model)

为了降低读取延迟,候选人提出引入“写扩散”(Fan-out / Push Model)。在用户发布动态时,Post Service 会将事件抛给 Fan-out Service,该服务调用关系服务获取发布者的所有粉丝(Followers),主动将新帖子 ID 扩散推送到每个粉丝专属的时间线缓存(Post line / Timeline Cache)中。这样用户主动打开 Feed 时,仅需单次读取自己的 Timeline 存储,即可直接拿到组装好的结果。

然而,面试官指出该架构在遇到公众主页(Pages)或大 V 时会遇到严重瓶颈。如果一个 Page 拥有 100 万甚至更多的粉丝,一条帖子就会触发百万级写操作。对此,候选人提出了“用户分级”(User Tier / Celebrity Service): - 设定粉丝阈值(如粉丝数大于 1,000 或 10,000 即标记为名人/大号)。 - 发帖时,属于该层级的名人直接跳过 Fan-out 流程,不进行写扩散。 - 在用户读取 Feed 时采用“混合架构”(Hybrid Solution):Feed Service 一方面从用户本人的 Timeline 缓存读取普通好友的推文,另一方面查询该用户关注的名人列表,单独拉取名人的最新帖子,最后在内存中将两部分数据合并(Merge)后返回给客户端。面试官认可了这一点,指出这正是 Twitter 所采用的经典混合演进路线。

```mermaid flowchart LR accTitle: Hybrid Fan-out and Pull Architecture accDescr: Hybrid pipeline showing fan-out for normal users and pull-merge for celebrities

subgraph WritePath["Write Path"] Author["Author"] --> PostSvc["Post Service"] PostSvc --> TierJudge{"Celebrity? (>10K followers)"} TierJudge -->|"No (Normal User)"| Fanout["Fan-out Service"] TierJudge -->|"Yes (Celebrity)"| CelebDB[("Celebrity Posts DB")] Fanout -->|"Push Post ID"| UserTimelines[("Follower Timeline Caches")] end

subgraph ReadPath["Read Path"] Reader["Reader"] --> FeedSvc["Feed Service"] FeedSvc -->|"Read fast list"| UserTimelines FeedSvc -->|"Query celebrity followees"| CelebDB FeedSvc -->|"Final Merge"| ClientReturn["Return 50 Feeds"] end ```

---

[29:50](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1790s) 面试表达策略与利用率算账

讨论告一段落后,面试官切换到中文,针对面试表达和沟通技巧给出了非常务实的复盘建议。面试官算了一笔账: - 读取 500 个 Follower $\times$ 每个 50 条 = 读取 25,000 条帖子。 - 最终只取 50 条,数据实际利用率只有 $50 / 25000 = 0.2\%$(录音中提及“不到 0.5%”)。

面试官直白地告诫候选人:“这个利用率太低了。你上来如果先抛出一个很单纯的规范化模型,面试官心里就会打一个大大的问号(Doubts)。其实你心里早已经想好了 Hybrid 方案,但面试是非常看重 Perception(第一印象与专业感知)的。你应该开门见山:‘设计这个系统通常有 Normalized 和 Denormalized 两种思路,由于纯拉取模型存在巨大的读放大与资源浪费,我建议一开始就采用兼顾两者的 Hybrid Solution’。”

面试官强调,即使最终把架构圆了回来,前期表述如果过于保守或从不合理的方案起步,很容易在面试官心中留下系统设计考量不周的负面感知。

---

[31:30](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1890s) 数据模式定义与基于 Redis Sorted Set 的动态流存储

进入实时性与多轮读取场景探讨。面试官提问:“在真实业务中,Feeds 是瞬息万变的。比如用户停留在页面上,其他人发了新动态该如何感知?随后的翻页(Subsequent Read)又如何处理?”

候选人梳理了客户端的几个典型场景: 1. 用户拉取了动态,停顿 10 秒后刷新。 2. 刷了一半,过了一段时间再继续刷新。 3. 早上打开看了动态,放了一整天到傍晚开完会又打开手机。

针对这些场景,候选人给出了底层存储的数据模型(Data Schema): - **Timeline 存储**:参考 Twitter 架构,采用 Redis 的 Sorted Set(ZSET)结构,为每个用户维护一个专属的时间线列表。 - **元素构成**:Member 为唯一的 `PostID`,Score 为时间戳(Timestamp)。 - **实体存储**:帖子内容、图片/视频媒体链接(Media Links)存储在后端 Posts Storage,前端拉取到 PostID 列表后,再去读取内容或请求专属 Media Service 渲染多媒体。

---

[35:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=2100s) 分页拉取方向与 Snowflake ID 假设争议

在讨论客户端后续拉取时,双方在请求方向与分页参数上展开了交锋。候选人起初提出,客户端可以通过携带当前最大 `PostID` 和 `Max Timestamp`,向后拉取比当前时间更新的数据。但面试官随即打断并指出一个常见的理解偏差: - 很多时候用户的下拉刷新(Scroll Down)并不是拉取更新的帖子(Newer data),而是在翻页查看更早的历史帖子(Older posts)。

候选人随即修正逻辑:当用户滑动到当前页面底部时,客户端可以提取当前页中最小的帖子 ID(Minimum Post ID),携带 `Limit`(例如单页 3 条)作为入参请求 Feed Service。Feed Service 在 Sorted Set 中查找 ID 小于该界限的历史记录并切片返回。

此时面试官进一步指出了方案中一个潜在的致命前提:“你把 `PostID` 和 `Timestamp` 混用来判断大小关系,这里存在一个非常大的前置假设——即你默认帖子 ID 是像 Twitter Snowflake 那样按时间递增生成的。但并不是每家公司或每个系统都具备或采用 Snowflake 这种强时间有序的全局 ID 生成器。如果 ID 不包含时间递增语义,你单纯比较 ID 大小就会完全出错。”面试官建议应当将时间范围判定(Timestamp / Score Range)与物理主键的依赖解耦,不要在系统设计中轻易引入未经确认的 ID 自增性假设。

[40:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=2400s) 动态 Feed 的 Ranking 与重复推荐问题

面试官与候选人就信息流的排序机制展开了具体讨论。面试官设定了一个业务场景:假设在初始阶段系统根据机器学习算法打分,向用户推荐优先级最高的 Feed,例如编号为 1、3、5、7 的帖子(数值越大优先级越高,代表算法预测用户更感兴趣的内容,以优化核心留存与互动 KPI)。然而信息流并不是静态的,后续系统可能会在近实时生成优先级更高的内容(如编号 2、4、6 的新帖、广告或高关注度用户的更新),候选人需要回答这些新插入的内容如何在服务层合并并返回给用户。

候选人最初设想在 Feed Service 层进行近实时合并,引入广告服务(Ad Service)以及特别关注(Interested Follower)等上游通知源。但面试官随即打断并澄清了核心痛点:“你还是没有完全抓住我的问题。”面试官指出,Feed 的排序分数处于动态流转中,数据来源五花八门——好友动态、广告推送、公共主页(Public Page)、同城信息或群组通知均有各自的分数。系统不仅要保证每次请求都向用户呈现最高分、最吸引人的内容,更关键的是**绝对不能向用户返回重复的内容(Non-repeating Feeds)**。面对这一挑战,候选人提出在 Feed Service 前置一个“过滤服务”(Filter Service),在每次返回帖子列表之前,利用布隆过滤器(Bloom filter)记录已读 Post ID 或 Ad ID,剔除重复数据后再下发。

```mermaid flowchart LR accTitle: Multi-source Feed Ranking and Filter Flow accDescr: Incoming feeds from ads, friends, and pages pass through ranking and a filter service before being returned to users. subgraph Sources["数据来源 (Feed Sources)"] S1["广告服务 (Ad Service)"] S2["好友动态 (Friends Post)"] S3["公共主页 (Public Page)"] S4["群组/同城 (Groups / City)"] end

subgraph Processing["聚合与去重 (Serving Layer)"] Rank["排序引擎 (ML Ranking)"] Filter["去重过滤 (Filter Service)"] end

Sources --> Rank Rank --> Filter Filter --> Client["返回客户端 (Unique Top-K Feeds)"] ```

---

[45:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=2700s) Bloom Filter 的适用场景之争:Sparse Map 与滚动过期

引入布隆过滤器的方案立刻引发了激烈的架构探讨。面试官直接提出质疑:“我们这里用 Bloom Filter 的具体 Use Case 是什么?”面试官阐明,布隆过滤器通常用于超大规模的“稀疏矩阵”(Sparse Map),用来快速判断某个元素是否存在于海量离散空间中。然而,对于常规用户的个人信息流(Homeline Feed),数据是系统为每个特定用户预先定制或推送的,结构非常密集(Dense),并不属于典型的稀疏数据集,直接套用布隆过滤器显得并不合理。

候选人据理力争,认为广告和公共主页大 V(Influencer)的动态对全量用户而言恰好符合稀疏矩阵的定义:大 V 拥有庞大粉丝群,单条帖子被全网某些特定用户零散浏览,这种浏览记录在用户-帖子空间中高度稀疏。为了避免布隆过滤器无限膨胀,候选人进一步完善了生命周期管理: - 推荐系统具有时间局部性,通常只推荐最近一个月或一周的新鲜内容,无需永久去重。 - 过滤服务可采用滚动窗口(Rolling / Scroll Service),以“周”为单位维护布隆过滤器实例,例如同时保留“本周”、“上周”和“两周前”三个过滤器。 - 键值设计采用 `User ID + Post ID`。随着时间推进进入新的一周,系统直接剔除最老一周的过滤器并新建实例,滚动淘汰过期数据,保证低内存开销。

面试官认可了按时间窗口淘汰过期的设计思想(“You don't want to filter it forever”),但依然强调在实际工业界方案中,必须根据数据特性采用混杂模式(Hybrid Model),不可一刀切地将所有帖子都推向布隆过滤器。

---

[50:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=3000s) Hybrid Model:Dense Timeline 的 Read Mark 与 Influencer 的 Bloom Filter

面试官深入剖析了混合数据模型下的读写差异:普通好友的动态在生成时通常直接推送到用户的专属收件箱(Timeline Table),在物理存储上表现为一个连续的列表(List)或表格。用户在客户端拉取自身的信息流时,本质上是在按顺序读取属于自己的定制数据。对于这种高密度的连续读取,使用布隆过滤器完全是“Overkill”(杀鸡用牛刀)。

正确的做法是在读模型上区分对待: - **普通好友 Feed(Push / Denormalized Model)**:直接在 In-Memory 缓存(如 Redis、Memcached)或数据库中为对应 Post 标记已读状态(Mark as Read),或者维护一个已读游标(Last View History/Read Offset),下次拉取时直接跳过已读偏移量即可。 - **大 V / 广告 Feed(Pull / Normalized Model)**:大 V 发布动态不适合全量 Fan-out 推送,通常采用拉取模式与 Join 操作。在逻辑和物理实践中,数以亿计的用户与单条大 V 动态之间是否已读构成了典型的稀疏关系,此时采用布隆过滤器或专门的 View History 检索才真正契合场景。

随着讨论进入深水区,面试官注意到旁听席中的林世耀(曾在 Facebook 工作五年的资深工程师),特意邀请他发言,希望能从工业界内部实际落地经验的角度进行点评与拆解。

---

[55:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=3300s) FB 资深工程师视角:Lambda 架构与 Ads Finder/Indexer 并发架构

林世耀开麦后首先指出,在真实大规模工业界场景中,不同产品的 Timeline 实现存在天壤之别。例如 Facebook 主站 Timeline、Group 的信息墙(Wall)以及 Instagram 的信息流底层架构完全不同,Instagram 甚至走的是一套代号为“Shots”的独立系统。

针对“低延迟且绝不展示重复广告”的诉求,林世耀对比了两种系统架构: 1. **通用业界方案(Lambda Architecture)**:采用 Batch(批处理,如 Spark)结合 Streaming(实时流,如 Storm)的架构。难点在于如何在低延迟下对批处理的全局特征与流处理的即时状态进行取舍与合并。 2. **Facebook 内部的 Ads Serving 架构**:内部采用高度并行化的 `Ads Finder` 与 `Ads Indexer` 架构。 - 当用户发起请求时,中央化的 Ads Finder 并不去查单体数据库,而是迅速将检索请求并发 Fan-out(扇出)给下游大量的 Ads Indexer。 - Ads Indexer 在严格的低延迟窗口(Timeout SLA)内并发检索并返回候选相关广告列表;若某个 Indexer 响应超时,系统会直接丢弃(Drop)该分支以保证整体可用性与响应速度。 - 最终由 Ads Finder 对所有 Indexer 返回的候选集进行二次聚合(Aggregate)与打分排序(Ranking)。

```mermaid flowchart TD accTitle: Facebook Ads Serving Parallel Architecture accDescr: Ads Finder fans out queries in parallel to multiple Ads Indexers, dropping timeouts, and then aggregates and ranks results. UserReq["客户端请求 (User Request)"] --> Finder["Ads Finder (聚合与分发中枢)"]

subgraph Indexers["Ads Indexer 集群 (高度并行检索)"] Idx1["Ads Indexer 1"] Idx2["Ads Indexer 2"] Idx3["Ads Indexer 3 (超时 Drop)"] end

Finder -- 并发 Fan-out --> Idx1 Finder -- 并发 Fan-out --> Idx2 Finder -- 并发 Fan-out --> Idx3

Idx1 -- "候选广告列表" --> Merge["聚合排序引擎 (Aggregate & Ranking)"] Idx2 -- "候选广告列表" --> Merge

Merge --> Finder Finder --> FinalAds["下发广告结果 (Ad Placement)"] ```

---

[01:00:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=3600s) 特征定向与并发渲染:Ads Insertion 与 Groups Wall 的本质差异

围绕林世耀的分享,面试官与他进一步推演了广告投放与普通 Feed 渲染的底层逻辑断层: - **定向匹配(Targeting Predicates)而非直接推送**:广告主在投发广告时,绝不会把广告直接 push 给某一个具体的用户,而是投递给一组属性画像(Predicates/Traits),例如“加州、18岁白人男性”或“中西部、50岁以上女性”。 - **双路并发渲染(Concurrent Passes)**:在渲染整个 Feed 的过程中,“Render Feed Content”是一路并发分支,而“Ad Placement Insertion”是完全独立的另一路并发分支。普通 Feed 高度强耦合于用户的静态社交关系图谱(Social Graph);而广告则弱耦合于社交图谱,完全根据发起请求当下的上下文信息与用户属性动态捞取,最后两者再在网关或聚合层合并输出。

最后,林世耀对比了业务逻辑最为简单的群组墙(Groups Wall)。早期的 Groups Wall 并没有复杂的机器学习重排与评分逻辑,其展示基本遵循反向时间轴(Reverse Chronological Order)。前端通过类似 GraphQL 的 API 从 Facebook 自研的 TAO 缓存层(TAO Cache)直接按时间戳(Timestamp)的原生顺序(Intrinsic Order)拉取数据,仅在应用层做极其轻微的规则调整即可呈现。这段经验分享揭示了超大规模社交网络中“普通信息流、个性化广告与群组信息”在架构复杂度上的巨大分级。

[01:05:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=3900s) TAO 架构解析与异构图(Heterogeneous Graph)数据模型

林世耀与主持人深入探讨了 Facebook 的存储架构体系。Facebook 主流系统大量跑在 TAO(The Associations and Objects)之上,TAO 本质是一个感知缓存层(Cache-aware),其底层持久化依赖于构建在 MySQL 之上的 UDB。如果系统需要强一致性(Strong Consistency),通常依托于 UDB;而对于只要写后读一致性(Read-after-write Consistency)或弱一致性的场景,则会采用基于 RocksDB(衍生自 LevelDB)的分布式键值存储 ZippyDB。这与完全基于 Apache Cassandra 架构、一致性模型较弱的 Instagram 截然不同。

主持人分析指出,TAO 虽然类似 Uber 的 Schemaless 具备自定义分区键(Shard Key)的能力,但它更核心的灵魂在于其异构图(Heterogeneous Graph)的数据哲学。在 Facebook 的世界观中,所有的 Feed、评论(Comments)、点赞(Likes)以及用户行为都被统一抽象为一张异构图。图中的节点(Node)可以是任何实体(Entity)——例如用户(User)、帖子(Post)乃至地理位置(Location);边(Edge)则代表丰富多元的实体关系(Relationship),如点赞、发布、归属权(Ownership)或基于位置的打卡。

```mermaid flowchart LR accTitle: Facebook TAO 异构图数据模型示例 accDescr: 展示用户、帖子与地理位置节点之间通过点赞、发布及位置关联构成的异构图结构。

subgraph Nodes["实体节点 (Entities)"] U1["用户: Scott"] U2["用户: 林世耀"] U3["用户: 景祥"] P["帖子: Hiking Post"] L["位置: Golden Gate Bridge"] end

U1 -- "发布 / 关联" --> P U2 -- "共同参与" --> P P -- "发生地" --> L U3 -- "点赞 (Like)" --> P ```

为了兼顾全球访问体验与性能,TAO 实现了极为复杂的多数据中心复制缓存(Multi-datacenter replicated caching)。其缓存分为单 Region 内的 Memcached 缓存,以及跨 Region 的 Multi-Master TAO 机制。虽然大部分标准场景倾向于 Single Master TAO 架构,但面对跨地域延迟挑战时也会启用 Multi-Master。多层级缓存体系的引入必然伴随一致性代价:在写后读保障(Read-your-writes assumption)失灵、Lost Update,甚至是可线性化(Linearizability)被破坏等场景下,极易出现用户 A 发帖后被删除,或用户 A 看到帖子而用户 B 刷不出来的边缘异常。

- **核心存储底座对比**:Facebook 主力为 TAO(前置缓存)+ UDB(底层 MySQL)/ ZippyDB(基于 RocksDB 的 KV 存储);Instagram 则是全球最大的 Cassandra 集群之一。 - **异构图示例**:Scott 与林世耀去金门大桥徒步发帖,景祥点赞。图中包含四个实体节点(两个参与用户、一个帖子、一个地理位置)以及若干关系边(发布关系、位置关系、点赞关系)。 - **多层缓存隐患**:复杂缓存拓扑极易打破线性一致性(Linearizability),不同产品线需在极端性能与数据一致性之间明确权衡。

---

[01:10:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=4200s) 一致性妥协、客户端 Prefetch 与极端严格的 Feed 延迟 SLA

林世耀补充道,不同业务团队会根据自身对性能和容错的取舍选择不同组件,例如纯内存、不持久化的 TACO 缓存服务。针对写后读一致性(Read-your-writes),大家探讨了移动客户端常见的"取巧"手段:用户发帖成功后,无需强求立刻从分布式 DB 中读回,前端完全可以进行乐观更新(Optimistic UI),直接从本地内存或存储回显。虽然这种做法在多设备同步(如手机发帖、电脑端刷新)时可能存在短暂数据不一致,但绝大多数消费级产品在多设备一致性上都采取妥协策略。

移动端App的性能优化更是将预拉取(Prefetch)发挥到了极致。用户在移动端看到的大量 Feed 并非纯实时从远端加载,而是应用启动时甚至后台常驻时提前 Prefetch 的结果。在探讨系统可用性与延迟的 SLA 时,林世耀透露 Instagram 的产品可用性 SLA 大概是 3 个 9(99.9%),但在延迟(Latency)方面把控严苛到近乎变态:在 Feed 场景下,哪怕仅仅引入 0.2% 到 0.3% 的延迟性能退化(Latency Regression),代码都极难合入,若达到 1.5% 的 Regression 则根本无法上线。

因为 Feed 流直接牵涉商业化变现(Monetization),卡顿一秒钟用户就可能直接滑走流失,因此业务层采用了许多工程黑科技。比如在图片渲染管线上,后端在下发元数据时会先将缩略图打成一段微小的 Base64 字符串随 Prefetch 下发;客户端收到后先渲染出一张非常模糊的占位图(Blurry Preview),随后真正的原图再由 CDN 异步加载覆盖,以彻底抹平网络加载带来的感知卡顿。

- **多设备一致性妥协**:大多社交 App 默认不保证跨设备即时一致,优先保障单机 Local Read 的瞬时体验。 - **SLA 的重中之重是延迟**:高可用 99.9% 允许每年有若干小时不可用,但 Feed 接口延迟指标(Regression)阈值卡死在 0.2% - 0.3% 之间。 - **图片加载黑魔法**:Base64 Inline Stream 占位模糊渲染 + 异步 CDN 全尺寸加载。

---

[01:15:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=4500s) 面试整体复盘:Hybrid 架构、系统设计方法论与选型推导

随着 Mock 面试接近尾声,主持人对李翔的表现进行了整体复盘。主持人指出,在设计大规模 Feed 流系统时,一开始开门见山直接定调采用推拉结合的混合模型(Hybrid Model)至关重要,这能迅速打消面试官关于超大明星账号 Fan-out 爆炸等一系列常规质疑。同时,主持人建议大家务必精读 Facebook TAO 的原厂论文,以及 Uber 从 Schemaless 演进到 Docstore 的工程博客与架构演进,深入理解大厂在数据模型上的取舍逻辑。

对于候选人李翔关于"面试中应在何时展开 Data Schema 设计"的疑问,主持人给出了系统化建议:系统设计绝不能一上来就盲目画表格写 Schema。最合理的顺序是先进行容量与流量估算(Scalability、QPS、读写比),根据系统是读密集型还是写密集型来推导选型底层 Database;选定数据库后,才能针对特定存储的特性设计 Primary Key 和 Partition Key。针对高阶职位(Senior / Staff),面试官考察的不是死记硬背的数字,而是基于隔离级别(Isolation Level)和一致性需求(Consistency Level)所做出的技术权衡。

```mermaid flowchart TD accTitle: 系统设计标准推导流程 accDescr: 从需求指标推导架构,再到存储选型,最终落地数据模型的自顶向下流程。

subgraph Phase1["第一阶段: 需求与权衡"] A["明确核心 SLA & 读写比"] --> B["权衡核心约束 (例如 Latency >> Availability)"] end

subgraph Phase2["第二阶段: 总体架构"] B --> C["宏观架构设计 (如 Hybrid Model)"] end

subgraph Phase3["第三阶段: 存储选型与建模"] C --> D["选型底层 Database (TAO / DynamoDB / Cassandra / MySQL)"] D --> E["依据存储特性设计 Data Schema (Partition Key / Primary Key)"] end ```

在系统指标方面,主持人点拨道,不必在容量计算上耗费超过 15 分钟纠结是四个 9 还是五个 9,因为在云原生和水平扩展(Horizontal Scalability)成熟的当下,增加数倍节点往往能解决容量问题。面试中真正拉开差距的核心三要素是:**宏观架构(Architecture)**、**数据库选型(Database)** 以及 **数据模型与 Schema(Data Model & Schema)**。

- **推荐阅读**:深入阅读 Facebook TAO 经典论文,以及 Uber 从 Schemaless 到 Docstore 的自研存储迭代历程。 - **Feed 核心难点缺失补充**:本次 Mock 尚未深入展开 Feed 排序(Ranking)与评分截断(Scoring),例如面对 500 个好友每人发 500 条、累计 25,000 条动态时,如何高效召回并"百里挑一"选出 200 条最让用户上瘾(Addictive)的内容。 - **广告精准推送逻辑**:林世耀提到 Ads 不会盲目 Fan-out 推送到每个用户的 Timeline,而是聚合为具有特定画像特征的用户群组(Group User Traits),通过群组特征倒排匹配目标用户。

---

[01:20:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=4800s) 问答交流:Bloom Filter 的内存实现与生产实效

在自由问答环节,有同学对前面提到的"已读去重过滤器"(Filter Service)提出疑问,好奇布隆过滤器(Bloom Filter)在工业界具体如何落地,以及在单机与分布式环境下的扩展性。主持人解答道,Bloom Filter 本质上是极度节省空间的内存位图(Bit Array),通常直接在内存中分配连续空间,利用多个 Hash 函数打点置位。如果希望共享状态,也可以将其外置在 Memcached 或 Redis 等内存 KV 中,但网络跳数会引入延迟。

主持人特别分享了学术界与工业界的数据:有专攻数据库方向的博士同学曾测试验证过,仅需分配约 8GB 的机器内存,Bloom Filter 在极大的数据基数下就能将误判率(False Positive / Duplication Rate)控制在非常微弱的水平。只要通过数学公式平衡 Hash 函数个数 $k$ 与误判率容忍度 $p$,单机 8GB 内存完全足以应付巨量已读帖子的去重任务,展现出惊人的工程效能。

- **Bloom Filter 底层机制**:通过内存 Bit Array 配合多重 Hash 实现,追求极致效率首选单机 In-Memory;集中式共享可选用 Redis。 - **空间效能**:8GB 物理内存已具备承载海量数据且维持超低冲突率(Duplication Rate)的工业级能力。

---

[01:25:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=5100s) Feed 排序打分(Ranking)、时间衰减与 Senior 职级定义

针对"动态存储究竟按产生时间还是按得分排序"的提问,主持人与同学展开了深入探讨。现实中绝不可能在每次用户刷新时把所有朋友的帖子全部扫一遍做实时重排。行之有效的做法通常是时间衰减算法(Decay Algorithm):一个帖子的 Ranking Score 是动态变化的,比如 10 小时前的 10 分帖子经过指数衰减(Exponential Decay)后,权重会大幅降低。因此系统往往在发帖分发(Fan-out)阶段便赋予初始打分,客户端刷新时则基于时间戳进行折扣衰减计算(Discounting),并结合未读过滤器(Unread Filter),按当前实际得分从高到低呈现给用户。这也是为何 Facebook 每次重新打开,前排 Feed 都会发生动态更新的原因。

```mermaid flowchart LR accTitle: Feed 排序与时间衰减流转 accDescr: 展示帖子生成后写入初始分数,经指数时间衰减与未读过滤,最终完成重排呈现的流转。

F["发帖时生成初始评分 (Base Score)"] --> D["基于时间差进行指数衰减 (Exponential Decay)"] D --> FL["已读过滤器过滤已读动态 (Bloom Filter)"] FL --> R["按实时折算得分降序排列 (Top-K)"] ```

最后,大家闲聊讨论了硅谷科技大厂评定 Senior(高级工程师)的工作年限标准。主持人解释道,Senior 职级并没有硬性年限门槛,跨度往往从 3 年到 15 年甚至 20 年都有。很多大厂将 Senior 视作职业生涯的终极职级(Terminal Level,即不强制继续晋升,可以一直舒适工作)。主持人列举了自己组里的真实案例:最年轻的 Senior 2 工程师本科毕业仅三年半,但这位工程师是美国本土人且 9 岁便开始写代码,工程积淀极深;也有人更有野心(Ambitious),三年就全力冲刺 Senior。总而言之,职级与工作年数无绝对正比关系,核心在于工程师是否具备"独立负责并交付任意复杂系统(Independently deliver anything)"的成熟技术担当。

- **Ranking 动态打分机制**:利用指数衰减(Exponential Decay)对历史得分打折,杜绝全库周期性批处理修改的昂贵开销。 - **刷新逻辑流**:未读过滤(Filter Unread)与时间衰减排序结合,确保用户短时间内刷新仍能获得新鲜且优质的内容。 - **职级哲学**:Senior 是行业普遍的 Terminal Level,年限弹性极大(3 年至 20 年),核心考核能否独立撑起全局技术职责。

## Notable quotes > [05:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=300s) We don't want to lose data. Data is everything in Facebook. — Scott Shi

> [10:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=600s) Let's go to architecture. I'm really interested in architecture here. I want to see how to handle architecture. — Scott Shi

> [20:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1200s) 我非常怀疑这种设计能否支撑高并发的 Scale。 — Scott Shi

> [29:50](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1790s) 这个利用率太低了。你上来如果先抛出一个很单纯的规范化模型,面试官心里就会打一个大大的问号(Doubts)。其实你心里早已经想好了 Hybrid 方案,但面试是非常看重 Perception(第一印象与专业感知)的。 — Scott Shi

> [29:50](https://www.youtube.com/watch?v=36-RK5RUMUg&t=1790s) 设计这个系统通常有 Normalized 和 Denormalized 两种思路,由于纯拉取模型存在巨大的读放大与资源浪费,我建议一开始就采用兼顾两者的 Hybrid Solution。 — Scott Shi

> [40:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=2400s) 你还是没有完全抓住我的问题。 — Scott Shi

> [45:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=2700s) 我们这里用 Bloom Filter 的具体 Use Case 是什么? — Scott Shi

> [45:00](https://www.youtube.com/watch?v=36-RK5RUMUg&t=2700s) You don't want to filter it forever — Scott Shi

## Mentioned Uber, Data Infra, Microsoft, Apache Flink, Athena, Facebook, Facebook TAO, Schemaless, Docstore, Grokking the System Design Interview, HDFS, AWS S3, HBase, MySQL, NoSQL, Twitter, Twitter Snowflake, Redis, Redis Sorted Set (ZSET), Memcached, Bloom filter, 林世耀, Instagram, Shots, Lambda Architecture, Apache Spark, Apache Storm, Ads Finder, Ads Indexer, Groups Wall, GraphQL, UDB, RocksDB, LevelDB, ZippyDB, Apache Cassandra, TACO, CDN, Scott Shi, 李翔, 景祥

## Video description doc could be found here: https://docs.google.com/document/d/12kmm7LdsI0RwmI4oV3mCv8TEplwAFZbXy7hL0cISE44/edit?usp=sharing

[Source](https://www.youtube.com/watch?v=36-RK5RUMUg)

中文简报

优步与FB工程师拆解Feed架构:纯拉取模型仅0.2%利用率遭弃 超大规模动态流必须在开局采用推拉混合模型,通过时间衰减打分与布隆过滤器过滤大V稀疏已读,否则跨分区读放大与千倍冗余查询将直接摧毁低延迟SLA。 视频记录了一场关于 Facebook 动态流(News Feed)的深度系统设计面试与复盘。双方从百亿读写容量估算、对象存储选型切入,深入推导了纯拉取(Pull)模型带来的读放大危机,并论证了普通用户 Fan-out 与大 V 用户拉取的推拉结合(Hybrid)架构。前 Facebook 资深工程师林世耀作为嘉宾参与讨论,剖析了 Facebook 内部高度并行的 Ads Finder/Indexer 广告召回体系、TAO 异构图数据模型、极度严苛的毫秒级延迟 SLA 以及针对动态 Feed 的时间衰减与布隆过滤器去重方案。 Facebook News Feed 等超大规模社交系统必须采用推拉结合的混合模型(Hybrid Model),对普通用户发帖进行写扩散(Fan-out),对大 V 与公共主页则跳过推送改为读时拉取,以平衡延迟与读写放大。 (22:30) 大厂 Feed 流系统的性能 SLA 极度倾斜于延迟而非可用性,Instagram 允许 99.9% 可用性,但 Feed 接口发生 0.2% 至 0.3% 的延迟退化即难以合入上线,超过 1.5% 则完全阻断上线。 (70:00) Facebook 底层将所有帖子、点赞与用户行为抽象为以 TAO 为前置缓存、UDB(MySQL)/ ZippyDB 为底座的异构图(Heterogeneous Graph)模型,并通过 Ads Finder 对 Indexer 的并发扇出和丢弃超时机制实现广告毫秒级渲染。 (65:00) 动态信息流防重复与重排可以通过时间衰减算法(Exponential Decay)避免全量计算,结合以周为单位滚动的布隆过滤器(Bloom Filter)在约 8GB 单机内存下高效完成稀疏动态与大 V 帖子的去重。 (80:00)

Reading summary

在规模达10亿日活的社交网络中,Feed流架构必须超越纯粹的拉取或推送模式,通过推拉结合的混合模型、分级去重以及异构图缓存体系来平衡极端严苛的读取延迟与写放大开销。 读写扩散的权衡与推拉混合模型 (20:00) 纯读扩散(Pull模型)在面对平均500好友的用户时,为了向客户端返回50条动态,必须向底层发起跨分区分片读取25,000条记录,数据利用率低至0.2%,且存在严重的跨节点读放大与延迟瓶颈。相反,纯写扩散(Fan-out/Push模型)在遇到拥有百万级粉丝的公众主页或大V时,单条发帖即可引发写操作风暴。 工业界的标准解法是推拉结合的混合架构(Hybrid Model):对粉丝数未超阈值的普通用户采用Fan-out推送至粉丝专属的Timeline缓存(如Redis Sorted Set);对大V与名人则跳过写扩散,在其发帖时写入专属存储,待客户端主动刷新时发起拉取,最终由Feed Service在内存中将两类数据归并返回。 去重过滤的场景适配与冷热分离 (40:00) 在解决多源推荐(广告、好友动态、大V与群组)引起的重复展示问题时,不能一刀切套用布隆过滤器(Bloom filter)。面试官与候选人澄清,用户个人信息流(Homeline Feed)在物理存储上是高密度的定制列表,采用布隆过滤器属于过度设计,更适合在内存缓存中记录已读游标(Read Offset)或标记已读状态。 相比之下,广告浏览与大V动态在海量用户空间中呈现高度离散的稀疏特性,更契合布隆过滤器的适用场景。通过以“周”为单位维护滚动淘汰窗口(如保留三周实例、键值为User ID + Post ID),结合单机约8GB内存配置,系统即可在极低内存占用与低冲突率下完成大V与广告的去重过滤。 广告分发与并行检索架构的分流 (55:00) 前Facebook工程师林世耀指出,广告投放与普通社交Feed的底层生成逻辑存在本质解耦。广告主面向的是属性画像(如地域、年龄、性别等定向前置条件),而非具体用户ID,因此系统无法预先Fan-out到用户时间线。 在Facebook内部Ads Serving架构中,中央化Ads Finder与普通Feed渲染并行触发。Ads Finder将检索并发扇出至下游大量的Ads Indexer,在严格设定的超时SLA内拉取候选集,一旦某个Indexer超时便直接丢弃以保全接口低延迟,最后经二次聚合与排序后,在聚合层与普通Feed内容拼接下发。 TAO异构图模型与极致的延迟SLA (65:00) Facebook主流存储依托于TAO缓存层及其底层MySQL持久化(UDB),将用户、帖子、地点等实体抽象为节点,将点赞、发布与位置打卡抽象为边,构成全站统一的异构图模型。为了规避多数据中心缓存层带来的线性一致性开销,客户端常妥协跨设备一致性,采用乐观更新(Optimistic UI)以及启动预拉取(Prefetch)策略保障单机本地体验。 在商业化变现驱动下,业务对延迟的要求极为严苛。以Instagram为例,系统虽然接受99.9%的可用性SLA,但对Feed读取延迟退化(Latency Regression)的容忍阈值卡在0.2%至0.3%之间。工程上常采用微小Base64缩略图占位随元数据提前下发,再由CDN异步覆盖原图等方式消除渲染卡顿。 大规模Feed流系统的面试与落地重点不在于死记硬背容量数字,而在于基于读写比推导架构分级、针对数据稀疏度选型去重算法,以及在延迟SLA与数据一致性之间完成明确的工程妥协;如何在大规模候选集中结合时间衰减实现高效打分与召回截断,仍是未完全展开的关键环节。

Source quotes

[5:00] Scott Shi: We don't want to lose data. Data is everything in Facebook. [10:00] Scott Shi: Let's go to architecture. I'm really interested in architecture here. I want to see how to handle architecture. [20:00] Scott Shi: 我非常怀疑这种设计能否支撑高并发的 Scale。 [29:50] Scott Shi: 这个利用率太低了。你上来如果先抛出一个很单纯的规范化模型,面试官心里就会打一个大大的问号(Doubts)。其实你心里早已经想好了 Hybrid 方案,但面试是非常看重 Perception(第一印象与专业感知)的。 [29:50] Scott Shi: 设计这个系统通常有 Normalized 和 Denormalized 两种思路,由于纯拉取模型存在巨大的读放大与资源浪费,我建议一开始就采用兼顾两者的 Hybrid Solution。 [40:00] Scott Shi: 你还是没有完全抓住我的问题。 [45:00] Scott Shi: 我们这里用 Bloom Filter 的具体 Use Case 是什么? [45:00] Scott Shi: You don't want to filter it forever

Original transcript

[0:00] 我现在是在Uber工作工作了Uber在Uber工作了两年多然后之前是在微软微软工作了一年半然后调查调调过来的Uber主要是做那个Data Infra相关Data Infra 你做哪一块啊 记得在乌版内部的 Flink啊, fling control我不是有分一个吗我一直有那个Athena是吧?对对对对OK OK make sense 嗯然后我们这边的话就是今天讲那个Facebook feeds啊Facebook feeds其实他也publish过一些paper我觉得比较好的paper就是那个陶Facebook陶这边paper还是挺值得大家读一下的然后啊然后这个这个陶这边paper呢就是我我9月份我去年11月的时候加入了一个Uber的一个strike team,当时也是sign了Illegal的,就不能disclose做啥。然后但是我们当时设计了一个graph,然后就写了一个graph,就写了一个relationship。然后和这个Facebook淘很像,但是我这写完以后才读到Facebook淘,然后发现其中好多想法是相似的。Facebook Fiz,如果大家感兴趣的话,可以对一本Paper叫Facebook的那个Tao,TAO。它也是inspire了很多Paper的设计。像Uber Schemeless也是Tao inspired的。然后Facebook Feet的话,中间还是有很多优化的然后还是挺有趣的,那我们现在就开始好了,然后你晒下你的screen,这样的话我们可以看着这个线。OK,so,这个description我是copy的那个,因为我自己不用Facebook,我Facebook的基本上我不用Facebook, 我就不喜欢把我的Data disclose, 然后就没有用Facebook, 所以我现在的话这个Face如果有什么新的Fature, 我不胜手, 上一次用已经好久年了, 14年, 13年的时候。好,现在的话就是我就copy了一个Glocking Interview的FacebookFace, 我觉得它设计的中间最重要的问题没有解答, 但是我们这块的requirements是copy在那里的, 所以这块的话,NewsFace就是OK,so if we want to design a new space,newsfades should be generated by some posts from other people.我可以申请一下权限吗?应该refresh一下,应该有的。你应该是有ID的前线的对吧? -Oh, cool. -OK,我应该给你了,对。然后,OK, let's design a newsfeed feed for the Facebook.so the newsfeed will be generated based on posts from other people and groups and also page other users follow and you may have many friends follow a large number of grouppages and group and the face me content image and videos and I'll just text and our service support support a pending new post as a right for the to the news feeds are for our users. So it should be另一位用者,和另一位用者,所以你也可以说你去去一些地方,你去到某些地方,你去到某些地方for example I can go to Golden Gate Bridge and leave a post there. And additionally, so you should be able to support you at new ads. That's the main revenue for Facebook. there is actually a lot of details, user experience details, but let's actually get started. Assume that you want to have a user have a non-broken experience, you want to have a good experience.you're going to have a lot of interesting requirements along the way Sure, sure. For the first one, like a user may have many friends. What is the average number for the friends? I think Facebook friends are pretty a lot. It's not like WeChat. Let's assume that you have 500.I think I read this part. I think they have total user is2.7 billionI remember 2 times 7 billion users2.5.5 is fine, yeah. They have that money, yeah. It's impressive. Monthly active user.OK,so for the day actively active user,how many there?Yeah,so let's for daily active user,let's just your one billion maybe.所以我认为 [5:00] The alignment here is quite clear. Let's first focus on the uh, We should have a How many? Like the K-- functions. Focus... Thanks. So user can post. and the user can get fit. Okay. Amen. And can also comment. and like. Thank you. So, For the post one, Suppose one user for one day, one day, one use of only postal one. then The total poster should be one billion. Thank you. And for each poster likes For the only text posted that could be... Oh. one KB should be enough, right? Can we suppose? - Yeah, one KB is probably for text posts. That's probably good enough. - Every time. So for one day, the data the storage will be Like when they... Eh... This is the one that like, This is... One can be. One, two, three. Okay. Thank you. This is one terabyte, right? I think it's around similar, roughly right level, yeah. Yeah, for one day, the data could be one terabyte. And suppose we need to store the data for five years. Let's assume that you want to stop much longer. I can still see my posts like 10 years ago. So you want to start forever, maybe. Forever? Yeah. But the system could be deprecated. in the long run. - System could, well, you can decorate the system, but you can migrate data to a new system, right? We don't want to last data. Data is everything in Facebook. Okay, now we can do this way like a week every year we are at a new storage so we can only estimate the data for one year. It should be dynamically horizontal scalable, right? That's a requirement, right? You want your service to be horizontal scalable. Yeah, but we will keep adding the storage on that one. like for the scalability is supported by the the architecture. Like we can-- easily to add storage. Sounds good. Yeah, we don't need to put all the data, all the storage there. in the beginning, so we can suppose one year. Thank you. So It's around the terabyte. Like we will also have some videos or images Like for this kind of post, can we support like user will have a less uh, post on that one. I think you actually have more video posts than image posts than the text posts. Okay. I don't know because I don't use Facebook also. - Yeah, you probably have more image and video posts and text posts. Okay, so that's a majority of posts, I think. One billion. For the video or the image, it could be much Baker. So, Can we... For the image, like one megabyte, Thank you. It's not. I think here it's you're storing in a different database, unless you want to store in the same database. So the storage size doesn't matter here in my own opinion. Okay, so you mean that we don't need to calculate this kind of things. Anyway, for this kind of files, we can store it in HDFS. So for the HDFS, it's quite flexible and scalable. Okay, so this is some part I don't really like. I think it's actually not a good design. So you don't want to start in HDFS because the Lunden C2 HDFS are optimized for throughput. It means that it's a-- it means that the [10:00] throughput for HDFS is a kills optimization. So it's like a TCP, it means that you can get your you know, transfer throughput maximum, but the latency will be very high. If you're familiar with HDFS, I think some of the call can take more than one minute. So Yeah, for the raw HDFS, then we need to find the directory and find the pass and get the get the file, but we can build it based on the edge base. - Oh, okay. - And the storage is still rely on the HDFS, but the the SQL engine, the, the, Yeah, will be the edge base. Okay. Thank you. uh, So, yeah, I think you can just mention you want to use like the out of box service like Amazon S3 and maybe add a cache on top of it. That makes more sense. Well, you can do your own service as well. If you want to rebuild the MLS3, that's your choice. You have an auto box service you can use. - Sure, sure, yeah. And also like for the For the video, it could be larger. Thank you. Thank you. All right, bye. This is gonna be a huge storage. For the QPS like we have one billion user and suppose one post, one every day. So on the And for one day it's about... 10 soldants. Thank you. Seconds. Oh, no. Thank you. 100. - One hundred thousand, right? And so the Thank you. Thank you. QPS would be one. - Be ready. Thank you. So it is a lot Okay. Thank you. This one. For the Yeah. Okay, I see. It's a user QPS. That's the right QPS you're trying to see? Yeah. That's right. Post-ocue. Post-ocue. Okay. So the peak QPS Let's see, we can multiply five. or 10. Thanks, Alron. Can you use one? So for the read, Actually, for the red QPS, it could be way higher than this one. Uh... Great. Thank you. Thank you. Thank you. Let's see one billing user - I think one day that could be, Thank you. Thank you. 10 times. to refresh the app in the website. So that one would be... Hi. Thank you. We can also still use Thank you. Thank you. Thank you. Yeah, I think it's a reasonable estimation. Mm-hmm. Thank you. Okay, so... Let's go to the Firstly, let's go to the API design. Let's go to architecture. I'm really interested in architecture here. I want to see how to handle architecture. OK, sure. Like, how was this? this one. So we can draw in on this one. suppose This is a user. And the user can host Thank you. uh service So one user do the post. Thank you. you can contact the poster service. And for the poster service, we need to have a storage - Uh... [15:00] there will be multiple since we need to start. Uh, Let's see. Post. It's... Thank you. - It's sale. And we also need to have the Let's keep the terminology thing. Let's call it feed, feed service. Thank you. It's a feed, right? Yeah. Sorry, Facebook feed system. it's a data system but the user needs to foster the poster the poster, right? And then for the like for the user, They try to get their feet. Then we have another one we can call it Thank you. Thank you. Fade this service. Thank you. Thank you. So this one will contact with user to Feather the Feeds We can go to the workflow first. Like look at the user can post the one. in the posted service. And the poster service will store store the data in the storage. The data contains the post the content and who posted this one. And also, like, for the poster, we can have some... video or image So for that media file, then we can putting in another like media. Store, change. Thank you. Thank you. Mm-hmm. Thank you. uh, So uh, The basic model can be all the posts are stored in the post storage. And then we have another We have this feed service this feed service will get some relationship. Let's see, relation. So for this one, if. it can provide the function like one user, we can find all the followers, followers of this user. And then You get the... All... Thank you. .com Thank you. follow each. So then the basic function could be, Zs1 to Thank you. This one can get the... Thank you. Thank you. Thank you. "Order for... Yes. Thank you. post. And then to do And not this. Thank you. Thank you. and then merge all the poster for the followings. So one user can request to the feed service periodically. And this one will get the user ID. to the relation service. the relation service can get all the user followees, then the follow the feed service used the follow-e uh, IDs to Uh, query the post and storage and retrieve all the data And here. because all the a full-year post could be sorted in the, in either by the timestamp or by the ID of the post. then we can easily merge it several [20:00] We can call it the timeline, right? Yeah, we can call you the timeline. So we can merge all the timelines. So this is the-- basic model, like, It's... It's called the... This is a... Yeah, this is now this. This is a relational model. This is a normalized model. I mean like One user want to get the fit. then it will find out the traffic. and to query all the flowyets and the query all the posts and the merge it. Yeah, this is a normalized model. OK, let's give you some questions. Some very interesting questions. Let's actually go something simple. how many posts per user you want to fetch. Like, For the Facebook, I'm not sure like, In one page, it's maybe only 10. Okay, and then we can multiply five. So Bye. who Okay. 50 hosts. Thank you. So, OK, so you want to get 50 posts. And then how are you going to do it? You have 500 friends here and how many posts you're going to get for each friend. Thank you. Here for each friend, in case some friends doesn't post anything, so at least for each friend, 52. So for each friend, for 500 friends, each friend, you get 50 posts. Yeah. Okay, so now you're actually reading 500 times 50. That's actually-- 25,000 posts and at the end you only return 50 posts. Mm. OK, so this is first thing, right? Second thing is that-- We want to optimize latency. So you can think of if your request is only reading only one database. It means that most of the time, like P99, probably 10 milliseconds. I... Yeah, for this one, we don't go in. to details of this database. They could be the noSQL so we don't need to do the sharding And also, we can also use MySQL or some other IDMs A relational database, then we can do the sharding on the service level. But still, you're actually-- So when you get post-- from 500 users. When you get posts from 700 users, you actually do a 500 API database call. Yeah. like if you're looking at the implementation and say this 500 API call, it could go to totally different partition and shardings of the database. So your reading call, when users reading call, you know, we don't see this amplification but if the read amplification is 500, Yeah, so I mean, this is a basic design. Then we can also do a find out to-- decrease the latency of the uh New state. OK, yeah, let's say it's optimization, because I highly doubt this design will scale. - Yeah, yeah. uh... once the user posted poster survey post poster. then we can do a found at this moment. So we don't let the user to actively pull all the posts. then we can uh, to generate the post-align. or the news feed line. for the for the followers. Let's see. We have another Let's see. This is a poster service. then we can send the poster service to fan alt service So this one, it will basically also do the same thing. Like... In the relations service, it will request the relation series. to get all the followers. Thank you. Thank you. Thank you. What about the numbers? And then for each followers, we can have a [25:00] line. This could be a timeline. And we can also have the post line, So, we can We can store all the timeline in the, like we can see the Catch. all the Stay the best. So... Thank you. Thank you. So with Once the poster is reached to the final service, And we'll... calculated based on the relation. and found out the poster to each followers. post line. So when the user comes to you, get the fit fit. Right. then it can go to firstly to check check the Thank you. Thank you. check the post line storage. and to direct if there is a post line there. for this user. then it could be returned the result directly. If not, then we will still go to the the basic line to get all the flowees and merge all the posts So For the final service, like it still could have some issue like you mentioned in the requirement there is a Page. And for each page, it could have a lot of followers. So like See, if there's one million followers, This file will insert to the 1 million users. post line. that it could be a big effort. So for that kind of service, we can also how Another we can call Uh... user. Thank you. Tier source So, it's basically store the Oh. either user or web page. Like, if the user or page have more than 1K or 10K followers. then we consider it as a celebrating. So in the founds, we don't do the Don't do the fine outs. we are ignore this, we are directly ignore this, operation. So If we add this one, then... in the field service, there will be a merge. for the last minute. There will be extra merge service. Oh, shit. So the workflow could be one user come to the feed service. Then the failed service will go to the catch and all the database. to get all the posts the post line for this user. Meanwhile, so it will also check the relationship service to find all the for the following is who is the we can call it a celebrity. who has a large number of the you Followers. Then, we can go to this one. to Get the large number of followers posts and then do the merge. So here, Then we can merge the post line with this one. and the return to the users. Yeah, I think this is a hybrid model, a normal solution. The number solution. This is actually also a design very similar to how Twitter is designed to their face. Yeah. Okay, so I want to say this is the "表述相关" [30:00] 但是这个利用率太低了 你看这个你读一个follower 500个follower然后每个在读50个post 读了25000个post 最后返回50个 这个利用率这个利用率只有不到0.5%0.59% 这个利用太低了,所以你肯定得说一下,我上来就叫一个hybrid solution,有nomalized denomalized solution你一上来就把这个酱的话能减少很多的Douts 是吧OK对这个跟表述相关其实你上来这些设计已经想好了但是这个跟表述相关就是你上来还是要这么说的这个会影响Perception,最后可能其实也没有什么影响,但可能会影响Perception嗯所以你上来的时候就你想好这个hybrid solution,就上来就说这个hybrid solution,这个的话还是很重要的好OK然后我想想OK,所以,所以,好,你遇到更新的新配置。例如,你读了一篇,有新的新一篇,你想要表明,I think when there is a new post coming, you will show someone who already uploaded, someone posted a new thing, you want to notify your user如果你再次投入了一张how do you handle that part because all the posts and feeds are ever changing不是不像是否有效果所以你意思是一 user already got the post line here, and then one of the followers already posted a new one, how do we notify this user?是的, how do you notify the user, and what about subsequent rate? How are you going to handle subsequent rate?嗯所以我可以与你相似的 scenarios所以第一次是You pull the post and then after 10 seconds you pull the poster again, you pull the phase again, so you pull the phase十秒钟,你再看了一个随时的随时,你再看了一个随时,你再看了一个随时,然后你留下了一半时间后一半时间你会不会Puza phase again第三 scenario is that you open the face at early morning and then it'stime goes by然后在后面你开会的一样,你会开会的一样,Data Scheme和Data Model像是for thePortline serviceand the storage we need to store like啊好for each user we can have a like suppose wewe are using the redis actually i think twitter user redisfor all this stuff所以我们可以用Rat is donefor each user it can have a post line在这个Post line 它可以好sorted the set谢谢谢谢谢谢大家and for this one啊forsorty set for each one we can usePostID谢谢 suppose this poster storage will supposeincrementalIDand also with a timestampand also the content一些像one poster could contains a photo or thevideo, then you could have a media谢谢大家谢谢Links嗯然后in the beginning, user can query the feed service, then feed service will return all the posts.for each poster it willWe also have the POSTID and TASTAtimes downand also other content media link嗯 then the media link will, a user can directly call another media service去找image, all the videosbut for this one like say在早上,他已经有了好一天后嘿可以去的 feedback service位置Let's see嗯好Current [35:00] post id And the Max. timestamp. So, use this to use this to keep Hmm. parameters, the field service can with from the the poster line storage and only extracted that the data bigger than the current post ID. Also, like, bigger later than this timestamp. Sorry, I got confused. So you assume that the post ID is actually time indexed? So the ID actually has timestamping it. Yeah. Okay, so what if you don't have any data? Like you look into the new timeline, you pull it very frequently. Like one minute you feel like 50 pulls and you want to pull again. For this one, when you use a poster into the poster service, it will find out to this. this cache or this DB, this storage. Right. Like, if this is missing here, Either this one is already-- this service is done. all the service has some legacy. here. Thank you. So my question is on how-- so here, you actually have a list. Do you want to keep the list, or do you want to keep the list ongoing? Oh For this one, it will clean the list for some time. OK, so you want to-- so the list is actually limited list, right? You will clean it up if it's too long ago. OK. In that case, when you fetch the data-- Help me understand this. Are you going to do the subsequent run will fetch the more newer data or the older data? The newer data. The newer data, okay. Yeah. But the first fetch will be the newest data. Yeah. Okay, so if you pull it a subsequent only, just, you know, after you pull the first page, you want to look at a second page, you don't get any data there. Mm-hmm. Thank you. Oh, you mean this one? It's timeline, right? So if you get-- I think one of the common use cases is that when you look at the first page of data, the second page data is like older posts, not like newer posts. Then we can like Suppose we already see the Like you can have this one. Suppose we already got the first page. Thank you. then we have the post ID for this page. and we can take the minimum post ID. And then we can use that one. uh, as a parameter to query the fit service. Also, we can add a limit. So it means-- Like, suppose we have a-- We have a poster 1, 2, 3, 4, 5, 6. Thank you. So for each page, we only return three. And firstly, it will query with the feed service Nothing. And then we can get the five, six, seven. And then when we screw down in the page. we can take the file. as a current post ID. So, We can also add a limit to like three. So when the fit service get this request, it will check the post ID smaller than the... the file. and retrieve three posts. back to the user. Okay, so here, I don't think, so I can do a post ID. Time stamp, so you should have a big assumption. Post ID, you can't show your idea. You can't show your time stamp. You can't show your snowflake ID. Twitter Snowflake ID, so post ID and timestamp has a big assumption. So I would recommend you not use post ID and timestamp, because this assumption is not... I'm sure Twitter Snowflake is like post ID and timestamp. and that's a not that companies have. If not, you may still use other determiners. to look at the range of the range I think my bigger issue here is that Um, So for example, I think the phase here--assume that you have phase [40:00] - Sweet. five seven. first. you know, that's an initial phase, like 1, 3, 5, 7. The bigger number it is, the higher priority you want to recommend to the user. Assume that, right? You probably want to add some machine learning there. I mean, Okay, you mean... Because you mean the poster ranking. Poster ranking, right? You want to have some machine learning there to rank the post. to make sure you give the user, because the ultimate KPI for your So your service is a user You know how how user going to you know, once they have to user or user like your platform. We want to recommend the user. Posted by people who care or please he care, you know, you want to deprioritate post if he's to next to you are some people he don't like. Right. So you do have some ranking post here. But here, let's assume that they offer to the first cops. You only help post one, three, five, seven. And then later, when you do the second part, you have two full six inserted. how do you go to return the result? I think the insert in the inserted poster should be merging on the fit side since it could be, uh, nearly real time. like see we have some Another service here. Thank you. Like it can could be the ad service you want to add ads. And also the Hapjeda um Trust either. Follow. let's say, like-- you mark someone as a interested follower. So you... once. this guy posted one poster, you want to get the notification. And For this one, Hmm. Like. Thank you. So we have the Pérez. When user queries the fit service, it can have the basic order of the posts. And then suppose Firstly, we support that All the ads, all the interested followers poster is not here. then we can use some to more disease based on the ranking. Like for example, even this one, this Let's see. So I think you still don't get my question. My question is the face, sir, you know, the ranking of the fit is ever changing. And how do you make sure your return You don't return repeatable feed. repeated feed and also you want to return the feed like most attractive to the user. And, you know, that could be another thing. I could say, you can set aside on like a ranking score for the SSL, you know, based on who paid highest price, right? Mm-hmm. Right. But generally speaking, you want to make sure-- assume that you have you'll have a fit list of different ranking scores. And the source of the fit can come from ad surveys. posting from ad service, the posting from your friends, of posting from public page and posting from maybe city information or group information. So you have a steady phase stream of different rankings got, but how do you make sure that You serve your user like non-repeating And each time you, You look at a phase that you return user, highest the rankings got, now it fits. So then we need to have a filter service. than to like every time before this service retains the poster list. we can go to this filter. Thank you. service. to filter all the retain data posts. in here. we can maintain We can make a bloom filter. Let's see. Thank you. Thank you. Thank you. Mm-hmm. Okay. Like for this one, in the bloom filter, we can use a You can [45:00] Post ID or the ads ID to do the filtering. However, there is a thing like... So why do we need a Bloom filter here? What's the use case for Bloom filter? Like one user can, suppose we return users this post line, one to seven. So the six, it is the ads. and then next time when he come back to query the fit fit service and get a new list one so we still have this as six here but we don't want to return the this ad to user again yeah so you don't want to you don't want to return something already shows a user right You don want to show the user repeated information But blue filter is you know in a sparse map Blue filter is in a sparse map You don't, you want to identify the potential mark, you know, if an object existing in this sparse map. And I don't think it's a sparse map here. Like, the data is very sparse here. Because you generate a feed for each of the users. not really a sparse right yeah so but but for the this could be sparse here I think you're still using the you know you still push your ads to the push your ads to the to the feed service yeah but I do see a point on this first map here as far as I think the public page and user public page post and the user maybe as far set sparse like if it's a public public figure for example it has a lot of followers if it if if influence answer publish a feed and and if you view the feed that probably a bloom filter I think that could be a potential include theater because that a sparse app sparse map Yeah Like also we suppose that the recommendation could be the recently one reason post, like we don't recommend one year ago or three months ago. So in this filter service, it meant maintain the Bloom filter, we can only store the data for maybe a recent one month. So we, for, let's say we only reserved for the one week, like the key should be user ID, plus the post ID. So see this, we have several things, like for this one, it's this week. This week means all the post ID generated in this week then the filter operation will go to this one So there could be last week. There could be two weeks ago. So every week, suppose we only maintain three weeks, then every week if this is we reach to the next week then we can just delete this one and then start a new one. So it's kind of a scroll service to deprecate the old data and only maintain the latest data. So every time there is a a new recommendation, it will go to the corresponding filter service to check if the user already got this fit. Okay, I think it makes sense. You don't want to filter it forever. Here, I just want to mention, I just want to mention, you do a blue filter, you should be a hybrid model, so in the blue filter, [50:00] 影响的Post,因为你这个UserFriendsPost其实是非常一个Count Dance的一个Poster所以那块你的filter应该是不用filter,用不同的东西来filter的吧嗯你 Bloom Filter 肯定是一个 sparse 的 map 你去 这个 map 就是这个你这个X轴和纵轴它这个点是比较零星的,你才去做bloom filter吧,你这个做的话像比如说那个一个 Influencer 的 post,然后对于所有的 user 有没有看过这 Influencer 的 post这个做Bloom Filter是make sense的 但你是个Hybrid Model嘛 你还有个UserFeed的Table你其实这个Feed Service 你里面上来说要一个一个读的嘛那你这个用Broom Filter就不make sense 吧嗯我理解这样的话也是很spass的你如果是那样子的话,你是个List,我觉得做Blue Filter其实是一个overkill,你这个谢谢我总体感觉是不适用于做 Bloom Filter谢谢大家你实际上就可以mark那个fit as read 吗?就是你read了以后你就把这个fit说我这个fit e read 过了然后你再读的时候跳过fit e 就行了吗?那它得是存在一个DB或者一个Cache里面对吧?就存在一个Redis,Memcache,找个Inmemory的DB就行了但是Cache它会一直clean的呀对吧? 开始会一直clean的,为什么会一直clean的? 因为我可能只maintain100个host line或者200个你把Post上面标上读过就行了谢谢谢谢大家谢谢但是比如说我的post,比如说我就产生一些add,我这个add不存在这个里面。我这个Ad就是用一些其他的技艺学习服务。你的Ad和Influencer的Pose,你是Hybrid Model吗?一方面是你有一个Steady的一个你有一个 sequence 有一个 feed 吗可能不是 influencer post 的那这个的话你就直接mark它读过Mail就行了另一方面的话是Influencer的就是有点用Normalized的Data Model来join那你这个的话就可以用不一种filter的嘛啊对对它已有的data在这里面是可以存起来的你可以存起来就是readnought就行了嘛就是已有的data加个boom filter的话其实你这个use case不对嘛就是boom filter不是sparse嘛Sparse Map这个不是Sparse Map啊如果把它变成一个Map的话这Map里面任何一个Post你都很快要读到了但是UserID加Post就是完全不一样的Post ID 它是连续的。是这样子吗?我想怎么说你读这个user homelight table的时候,你不是从一个sparse的一个map里读的你logical上是一个sparce map就是从逻辑层上是个SparceMap的但是你从实践层上你就从一个table里面读的就从table里面读出给我准备好的一个Fade所以你读的时候就是读这个Fade然后是给我准备的Fade就是这个所以你其实不是一个Sparce Map,但是你要读比如说这个Influencer的Post和这个User之间关系是不是可读的,这个是个Sparce Map,因为它这个实践的时候,你也是个Sparce Map。对,这就有你们的确是我们可以存在这里面对吧是这么说的吧嗯谢谢大家我看林世耀在,林世耀,你这话有什么额外要说的吗?点评的吗?因为世耀在Facebook工作了五年了请不吝点赞 订阅 转发 打赏支持明镜与点点栏目请不吝点赞 订阅 转发 打赏支持明镜与点点栏目谢谢大家- 大家听得到吗? - 冷听得到?- 我刚刚在干活,没有太认真听 - 好,阿信你肺子没有那么熟,肺子比较熟一点,肺子没有那么熟那个谢谢你们的问题是什么?他这块用了,也没有什么问题吧,就是看有什么做的不一样的地方,这块是用了一个Normalize Model和Denormalize Model,然后Normalize Model是Influencer的话用个Normalize Model,所以他读,如果是你读Influencer的Post的话你是 join 他的 post table 然后是如果你是读Friends的话,你Friends不是一个influencer的话,它是把那个data翻到你各个user的homeline,home table,就是那个home page的这个timeline table里面然后你读的时候是从这个TimelineTable里读然后在读上你的那个你follow的influencer的post然后 [55:00] 你要鉴别怎么样是哪一部分是可读的话哪一部分已经读过的话是如果是Influencer的Post的话这个Sparce Map是用这个是Sparse Map所以是用这个Bloom Filter来看你是不是读过这个Influencer的Post吗然后其实不用写的Filter行,你直接读一个Last View History行,就是View History也可以吧,读起来。然后如果是你给你生存的这个Homeline,就是Home的timeline table的话,你就是读过的话就把它mark 从读过,然后你只说的subsequent 读的话,你是跳过你读过的fism然后这边说的费是用rank费,用machine learning,不管是machine learning,来自于machine learning,还是来自于这个 ads,就是如果你想给一个user的Home的Timeline Table里面Insert费的话你要给我一个feed 然后content 然后再加一个recommendations- 然后他按这个recommendations-更relevant的更排到前面,然后less relevant排到后面一点嗯嗯谢谢大家好就是听一下你的想法因为我感觉你一直在工作这么久有什么comments啊嗯我是有点好,我是...我不知道怎么讲啊,我比较熟在这样子,但是我知道每一个就是大家看到的Timeline,或是Wall,或是Feed,他们Implementation可能都不太一样,像IG跟FB的TimelineImplementation也不一样,但是IDEA上应该是就是有Recommendation,有Score,有Ranking那个...大部分是有的,但是我印象中每一个系统好像是完全不一样,就是groups的那个叫wall,然后beats的是我自己的一套,IG的又不一样,IG的全部都是走另外一套系统 叫做Shots所以我现在因为我刚刚没有完全发我中间这个design 这些失误,所以我刚才有比较严重的bug,所以我现在比较没有办法直接等会扛我的东西。好吧,没事。你可以简单说一下,就是里面大概怎么设计的,high level 的可以讨论一下,怎么样设计的,简单聊一下。OK- 嗯,不可以啊 - 可是好要call infra的东西哦,call infra的东西因为最早版本跟现在已经应该差非常非常远了,最早版本跟现在差非常远那个好谢谢如果是什么?主持人:取历阿子好了 阿子好像就是你想要不秀给user就是repetitive的东西的话那就是基本上你是在做一个叫做realtime streaming system而且要low latency就是你的那个Response Time要非常非常快就是所以那个好像Industry有两种做法嘛 一种做法是就是用那个batch加Streaming的那个project 那叫什么Longda Architecture- 哇有点剧起来那个... - Lambda architecture,叫lambda architecture,叫lambda architecture,这是一个常见的做法那困难点就在于你又有real time 的东西,然后又有一个batch 的东西,例如说spark + storm,那你中间要怎么去?去取舍,因为Batch,Catavistic,跟那个跟Streaming不一样那这个是业界常见的状况,但是我记得Facebook的做法是完全不一样的他做法就是有个 serving system叫做做 as index, as finder, as index那他是centralizer让那个叫做AdSFinder就是它是一个as finder 非闹到很多as indexer有query的时候 read的时候就不断的建index吧大概high level是这样就是indexes is highly parallelized然后他们之间就是就是本来说 user information is not as find as finder 非闹到很多 indexer 然后fetch 在一定的时间内 return 就是relevant as list [60:00] 那就譬如说非常多个例子Return回来 那如果太久Timeout 那个就直接就可能就drop掉 然后而Finder之后还要再做一次Aggregate就是所谓的Ranking的部分,所以Sourcing的部分是FinderSourcing给index然后return之后再merge,然后那个叫做Ranking的部分,那IG又整个不一样了,他的做法是谢谢他的作法他中间层是一个叫下的东西,所以他不是反就是Finder index他是有个叫Shots然后呢他的另外一个Ranking是一个叫什么啊 叫Barkey反正那个就是high level 它里面很多细节又不太一样了 我要复习一下 我才能够把它点评出来OK 所以它这款其实还是把它翻案成了吗 就是您给一个案子然后写到 相当是写index 就是不一定是直接写到user timeline但是它其实是写到user的predicate的一个排卵就是比如说18岁我懂了就是这个爱子比如说你post一个爱子说我不是说直接卖不到这个user我更多是卖不到比如说18岁男性比如说白人然后在这个California然后我读的时候如果我满足这个user的trades和他满足我就读到这个爱子是这样子吧谢谢大家类似的他就是他基本上不会就是因为我刚刚没有follow你们那个table怎么存,他不会直接把哪些东西直接存在一起所以它其实增加了一个中间层就是把user呢就是把那个孩子呢对,他add placement insertion是另外做的,就是在render feed 中你可以想说RenderFeed Content是一个一个Concurrent Pass, and Adds Insertion是另外一个Concurrent Pass.所以这个比较make sense,因为你其实这个Ads的Feed确实不一样嘛,Feed的话你可能也有根据其他的trades来做。但Ads 的话这个确实是比较相对来说也比较intuitive因为你post Ad的时候你要说比如说我这个target的用户就是50岁以上的女性是吧50岁以上中西部女性是吧你就post到50岁以上中西部女性然后你就那个scout也比较高一点是吧然后如果你来一个user的话你就读这个50岁以上中西部如果是满足这个的话你就会读到这个是吧但是你post的时候其实是你ads其实是没有说是直接把ads push到user的timeline但你是push到一个user的tradestimeline读其中几个trades的话你会adjust这个score然后再决定读不读然后最后的话aggregate一下然后如果bounce的话timeout就timeout所以feet的本身跟social graph比较titedly couple,就是你现在跟你的那些friends,这个static,就比较static的那个social graph的状态就是会去driven你的feet怎么显示,但是阿子就问你,比方阿子跟你当时发的那个request 当下的一些讯息所以跟你Social Group其实是比较远的,就是他并不是Anchor了,Social Group,就是我大概如果要很High的话是这样可是Groops就简单很多,Groops基本上他那个叫Mo那那个就比较早期发现 我记得应该是chronological就是reverse chronological的做法他比较没有缩行的Ranking,他是一个比较基于query吧,就是...怎么讲啊,query就是有一套系统有 API 类的叫 GraphQL 但是它本身的 return 的那个 order 是它的那个有一个 intrinsic order 那个就是怎么说啊 就是 GraphQL 是基于在 tau cache 类的上的东西 那 tau 每一笔资料其实有一个 time-send 股 所以那个 time-send 是它的 intrinsic order所以那个Response回来的时候如果你没有加条件的话一般就是那种Crincy Order 所以是在Application Layer 再去调整它那个就是显示的那个顺序 就是这好像是Groove的作品 他们没有太Fancy的东西这边的话,Facebook现在主要用的是TaoDB还是CassandraDB我 [65:00] G是在Cassandra上,Facebook大部分是在TOW上,TOW是一个cashwareTaus cash的底层是一个叫UDB的东西基本上就是至于基于MySQL吧 如果你要有strong consistency的话是基于UTV 但是如果你不需要双 consistency 例如你只要 read after write 这种 consistency 可能会有一些 KV ストア像是ZPTVZBTV是基于ROXTV的东西,ROXTV是基于LevelTV的一个改版东西所以那个...所以这个就是...怎么讲?就是跟IG完全不一样,开G是完全基于Cassandra,所以IG在Consistence上就会相对弱嗯OK, makes sense, makes sense.这块的话,这个TaoDB其实跟Uber的Schemeless有点像,但是它增加那个shard key,所以它你实际上是可以specify the partition key 的,然后其次的话TaoDB它不只是一个DB 它实际上也把它就是它这个Data Model和这个Philosophy写进去了它这个TAUTP是个Graph他认为这个他认为所有的Facebook的Fadescomments Facebook like 都是叫 Hintergenius Graph 或者是Hintergenius Graphs 翻译成中文就是hintergenius graph 说白了是 你这个node可以是一个user你这个graph里面弄的可以是个user 可以是个post然后可以是一个location所以你比如说假设我和林世耀去一个月前去Golden Gate Bridge 然后去hiking了是吧那我post 一个状态然后另一位同学景祥表示like这个post那其实这个就是个graph嘛,这里面的entity就是我和林赤耀,然后另一个entity是什么呢,Golden Gate Bridge然后另一个atelier是什么呢?就是这个post本身然后还有一个like呢like是一个relationship就是在那个graph里面一个edge然后这个 entity 是锦箱所以相当于是我和林是要作为两个 entity同时在这个Post中被就是related to post, and this post related to Golden Gate Bridge. And this post is related to the Golden Gate Bridge.景祥跟景祥的关系是被景祥like了所以是relationship是like所以它变成了一个intergenius graph嗯就是它这个里面Hintergenius Graph说的node可以是任何entity然后这个relationship可以是like 可以是post post然后可以是跟这个post相关也有可能是比如说这个location是吧,位于这个location,还有很多其他的可以有可能是ownership,这个relationship相对来说比较多元化一点,所以它这个TAUDB不只是sharded mySycle加一个Caching那样它Caching其实它那块写的也挺复杂的写的挺多的它是一个Multi Data CenterMarket Data Center replicated cachingMARTI data center replicated caching然后他同时也把这个Graph的Data Model的Philosophy写进去了它的Cache蛮复杂的Cache有好几层,它有1Region的Cache叫MemCache,有MultiRegion叫做MultiMasterTal,就是每个Region有一个Master,那FindTune就是看你Region要切得多细但是大部分装潢还是Single Master Tau 吗?Mori Master Tau 只有在那个就是你Performance 就是你有一些latency的concern其实就没有办法用一个很简单的设计来盖国外所有的状况,我相信应该是不同的状况他们用的Cashware都完全不一样一定会所以那个不太能够用几句话 盖瓜起来如果有这么多开始的话,有几个常见的问题是,你刚写的post,radial right assumption,其实是比较难guarantee的。所以一个是radial right assumption,其次的话,你可能会出现lost update,或者叫lineability可能会被violated。比如说你读的时候你朋友读的时候你刚post了一个post然后又delete掉了或者比如说你刚post了一个post然后你user A 读了以后,然后看到这个 post,然后user B 在读的时候却没有看到这个 post,这个其实是就是 violation of the linear ability 是吧 我刚刚就是上周四刚刚谈过 violation of linear ability其实这个是比较难 guarantee的可能需要 special 设计但是如果你没有设计好的话因为你有这么多层cacheRadiol Red的这个 [70:00] 这个 violation 可能也是有的就是你刚写完以后在读的时候读不到你的 right所以我觉得这个可能不同的没错我觉得他们是不同 product team 之后决定不同 take the style如果你只看 performance 的话大部分就是例如说选TACO,TACO就是一个Pure in memory的Cache,不是Persist然后anyway就是这个要跟每一个team谈过的知道他们女主要选什么才会知道他们所有就是Trader无常牺牲的哪些那些东西那这边的话是不是你作为一个user来说他所有读过的费子应该是要被记录一下了吧就是他user读过哪些费子是要被记录一下了吧嗯应该是有的肯定OK我以为对于read your rights这个东西其实有很多hanky的方法吧就比如说像facebook这种东西你你post上去之后你其实前端可以做一些tricky的东西也不一定毕竟全需要DB存储存的前端的话会有一个问题是你可能是multi-device的就是你比如说你手机post了一个然后用这个desktop pool了一下然后没读到的话也是影响user experience对,但那个是小case,大部分产品并不保证multi device,这是我的理解。很多产品组他们并不保证consistency of close device谢谢对,简单确实也可以做的,就是你读的时候,实际上你就不从DB读了,你读的时候就从Local读就行,从Mobile上读就行谢谢大家Mobile部分他们的optimization我相信绝对不concision,因为他们好像是in favor of pre-tatch所以其实你Mobile上看到很多data都是事先读好不是real time 所以通常是你在StartApp的时候就prefetch了很多东西 所以我觉得你的看到的绝对是比较delay的就是主要是在App Store还是好?我有几个问题还是挺有趣的就是你们来说就是Facebook里面来说读Ads的SLA和读Face的SLA一般是多少?这个我没有当他们很 close 所以我不知道我知道那个IG、Prada、SLA是三个九 没有很大就是availability是39,但是latency的SLA呢?Latency看不同的产品,如果是fee会特别太大谢谢Feed,我记得好像,我忘了它实际上速度好,但是它regression是很严格,我记得好像0.2,0.3个regression都是有问题的1.5%的requation就是Lacency regression基本上就过不去了所以他这个SLA是Lentons的SLA是非常严格的是吧但实际上他们现在的数字因为我们去他们主张 code 我有点忘了 但是我大概知道他们 latency 是费上比较可能剩 latencyOK OK OK 相对来说这个Lantins也肯定还是很影响的因为你毕竟是monetarized的这个你要读的一秒钟没刷出来可能就不看了所以很多Hacky的东西居然在Application上做,例如说Image的部分他们有那种就举个例好了,我知道现在还是这样,我以前看过的,他们有把image hash成一个day64,就是一小段Stream,然后那个在prefetch的时候就是就读下来,所以你就有一个很 blurry 的 image 就先 display 然后之后那个真正的 image 才是从 CDN 过了。就是为了优化这个 CDN 这个 Lentense 就是先读一个 BIS 64 的然后显示一个非常模糊的,然后再从 CDN 读到 image 来读。对,可是这应该也是好一阵,不知道我们现在有没有,再去改进这个事情。因为Facebook很多practice跟industry都不一样像Long的architecture我们就没来用可是我觉得...Labda architecture挺常见的其实它虽然是最早好像是Twitter的一个engineer推出的但是其实应用的地方挺常见的 [75:00] Uber有一个Lambda architecture是PigitPiget其实也是一个Lambda architecture大家可以看到的事其实是 算是提出的时候是for whatever reason,但是其实在工业界应用还挺挺常见的谢谢大家好,对啊,我觉得相对来说还是比较有趣的,就是你其实这个中间这个Facebook这个怎么样做这个费费、management,然后对他费费费然后像蜥蜥线管理的那个Cassandra nodes也有四五千 应该是最大的Cassandra nodes好Cluster好的我们还有五分钟啊,然后我这边说一下,我觉得就是李翔这个设计,我觉得没有问题的,就是中间有一些discussion也是没有问题的,然后然后我觉得表达的话可能上来的时候就说你要做一个hybrid model比较重要我觉得这样的话否则的话面试官可能一上来会有很多问题但你要上来说是个hybrid mode 的话我觉得可能面试官的问题就会少很多然后我们这块的话没有cover一些feature design就比如说是怎么样设置一下comments 怎么样设置一下like然后这些没有cover 但是这个的话其实大家我觉得最好的还是读一下那个Facebook的Paper 这Paper其实写得非常不错我觉得写的非常不错的对于问题理解也很深然后你读完这个Tot paper然后你可以在subsequently读下这个scheme list然后Uber Stimulus Designs,也有Engineering Blog的没有写配备有个 Uber-EngBlock, 这些是 这个然后你可以想一下为什么如果Uber的同学的话可以想一下为什么从Schemless到Docs到了这还是有原因的 从Schemless到Docs到还是有原因的并且你可以就一下Dogstar的这个设计好他怎么样做的那些implement这个其实好像你就直接search Facebook 讨论我也忘了哪个期刊上的就直接search 也可以然后这把设计我总体来说我觉得思路还是蛮清晰的有一些detail的事情就是可能要再写一些细节比如说你可以这块的话我觉得可以写一个对于一个Face来说一个Life cycle是怎么样的然后对于一个User来说你来自于不同的Face是什么样子的然后这块的话用FacebookFace的话你肯定还是要提个RankingRanking Facebook Facebook Ranking和Scout这个其实没有提到吧 这个其实明显是很重要的一部分因为你这个50个user每个pass 500个user每个人post 500个face 其实25000个face那我怎么样显示200个face 是吧 25000 怎么样白里挑一选到200个就让user addictive的这个adicted这个feeds其实是非常有研究的 然后可以优化很多的然后对 然后今天的话施耀也分享了一下这个就是现在Facebook用什么database啊,我觉得这还是挺有趣的,然后包括怎么样怎么样把这个Ads push到Relative User它不是push到每个User的这个Timeline 它是push到一个一个group user,把group user traits,然后从group user traits找到更matched的user,然后push过去好李翔你有什么额外想说的吗好我就想问这个,因为我之前跳槽的时候,那时候面的系统设计其实也挺简单的,因为当时在工作一年多。然后现在这个就是,因为这次Mock就相当于不是那么的完整,就是一般design的时候不都会涉及到data schema?怎么设计就比如说这里面有好几个storage,每一个storage要怎么设计这个一般是说我们给面试官说完了这个OEU的architecture,然后就开始进入底层的这些东西。对,schema是和database相关的,你必须根据,就是这边的话,首先我觉得流程最好的是这样子,就是你首先计算一下这个scalability,就是你QPS是多少,然后ray的write ratio有多少然后read QPS 多少write QPS 多少然后再根据你的需求选择一下这个database是吧你是read heavy 还是write heavy 然后这个选择一下database然后根据database的设计schema因为你DenemoDB或Cassandra的schema和MySQL的包括sharded SQL的都是完全不一样的如果你用Facebook淘的话,你partition key怎么设计是吧包括你用Presenter, PartitionKey怎么设计, PrimaryKey怎么设计,也是不一样的。所以这个Schema,DatabaseSchema肯定和Database是直接相关的嘛。然后所以这个甚至有时候可能设计完了以后发现之前的不对可能也要退到重来一遍也有可能就是你最后你最后所以一定要把那个requirement这个tradeoff写到最前面这个tradeoff写到最前面你比如说这个的话我们 [80:00] 看这个Facebook这个Face的设计其实这个Lentense也是更重要的availability 3.9其实很糟糕了 3.9一年可以到八个小时这个availability其实很糟糕但这个lentency要是多0.2%都是过不了的,Lentense其实是最重要的所以你必须把这个trade off写到前面然后再选architecture 设计architecture 选database然后再设计dataschema如果不work的话你退导重来一遍这个是很正常的但是算这些东西的话就直接就超过了15分钟我估计我个人建议算的时候快点这个算的其实相对来说说实话你是四个九还是五个九我并不care十的四字方也十的五字方我并不care因为这个horizontal scalable现在都是用horizontal scalable的你这个对于淘来说你加几个server或者减几个server就是你比如说四个酒变成五个酒你可能加十倍的server反正都无所谓的像Uber的Skinless也是一样的四个九五个九并不是很开玩意你可以自己把这些时间压缩到几分钟之内就表示你有keep this in mind,最主要的是最主要的三个方面是Architecture, Database和Data Model然后Database那块的话你很多时候是要根据你的Isolation Level consistency level 然后来选这个database所以这块的discussion可以很多也可以很深入的Data Schema的话Data model和Data schema的话 你必须在Database之后才能行因为不同database的schema是完全不一样的我觉得对然后并且这样的话一般来说你看你面试什么level了如果面试senior level的话任何问题都有可能问的Senior Level是任何问题都有可能问的好因为我觉得Comparsense的瓶颈期比较早到CNR就是个Terminal levelC年往上是不要求的所以到C年来我就supposedly能做任何independent做任何东西的,所以任何问题都有可能问。我去所以这个对可能这个System Design会更难一点好的谢谢大家我没什么问题了。好的,我们有什么其他东西有什么额外的问题吗?我可以回答一下。或者直接说就可以了谢谢大家优优独播剧场——YoYo Television Series Exclusive没有的话就到这好了我再等试验走没有的话就到这好了有个问题,你刚刚说的那个Bloom Filter,你打算是怎么做呀?就是这个Filter Service,你是要用什么DB还是想用别的?就怎么做这个东西? Bloom Filter 是 Memory 啊你就直接申请一个memory块,然后里面都是一个bit好那是不是这个就不太那么scalebloom filter是为了审内层啊你也可以用什么memb cache或redis来做甚至你可以用db来做但是就会慢嘛然后分键成那你那个就只能在单台机器上了对吧就是你就是在inmemory单台机器上搞你可以单台机器对Bloom Filter 可以非常Efficient,我记得好像那个群里面有一位在研究Database的同学发了一个PhD,发了一个Bloom Filter,我看了一下他说8GB的Memory,其实这个Bloom Filter的Duplication Rate就很低了可以算一下我觉得它有几个指标的一个是K一个是Value还有一个什么有个P值什么的,反正就是它有不同的但是从前来说你有8GB的memory,它这个duplication rate就很低了你可以读一下那一块,我觉得这个还是挺efficient的之前有人发过 还是挺efficient的你有什么问题吗 轻易我想问一下,就是那个,在那个,就是Stall,在各种用户的费用时,通常是按时间顺序去费用,但是在买的时候,它的排式有可能是按照一定的位置去费用这样的话其实这个start的时候就应该按ranking来startstart的时候就按ranking来start你读的时候就按ranking来读 你如果按时间start来ranking来读的话这个本身就不知很合理哦,我试,我试你知道的时候你没必要钱是最新的吗只不过最新的ranking会更高吗你比如说你的RankingScore里面可以可以你可以你的Ranking不一定是固定死的值啊你的这个Ranking可以加一个Timeindication 就是你ranking 比如说10小时前ranking是10的 [85:00] 和一小时前ranking时的你肯定会decade它吗你可能会有个 decay algorithm去 decay 它的 0r ranking 值所以是在他fan out的时候就已经把ranking算了?你的ranking只是说这块有很多优化的地方了,我直观猜想就是你ranking,比如说你10小时前ranking是10的,然后那你和一小时前ranking是实的那你肯定有个decaying就是decay一下之前的ranking是什么你用个指数型decay就可以了嘛对然后我发现这个Facebook要是我打开 第一次打开然后过5分钟再打开,然后前面的5个feet都是不一样的。我每次read完之后,它这个ranking都会upgrade。就是我可以多一会儿的对,然后它Ranking肯定和时间是相关的吧,你最后的RankingScore肯定和你读的时间相关吗?因为你ranking score 你不可能说每小时以后再把user的ranking score全部改一下然后把所有的userranking score降低个1什么的 这不可能的所以你ranking stock肯定是有时效性的然后你读的时候跟随你的时间把那Ranking Spot discount然后你读的时候你refresh的时候你肯定是说我把所有的就是没有读过的我现在对这个user来说我把所有的没有读过的费字然后再按ranking的从高到低然后显示一边我觉得这样比较make sense 一点。 哦,对对对,它可能是先filter and read。 然后这块有个同学问几年算senior,这个其实时间比较flexible,有十几年多的senior,也有三年的senior,两年的senior。我们组最年轻的一个senior2只有三年半的工作经验,本科毕业三年半的senior2了。但是他是美国本地人然后他其次的话从9岁就开始编成Local,然后九岁开始变成,所以他三年半就送到CDR2了。CDR的话,我觉得是从三年到十五年,甚至二十年都正常了。Depends on you want to take the responsibility你有时候有非常非常有野心的人,ambitious的人,可能三年就想面一个CNR之边,有的人他看着不一样,可能25年的话做CNR也挺好的好所以没有一个hard answer底盘知道你自己觉得能不能做这两个好 如果没有额外问题就到这里好了 今天非常感谢大家 然后下周还有一个Mock-Interior,下周Mock-Interior candidates比较强,他上来说自己想做Candidate还是可以做Interviewer都可以所以我们下周到时候看讨论什么题目他应该有很多interview我的experience所以我也不知道我能不能interview他或者说可能他出的想法比我更前面一点我还是比较期待的下周的这个interview非常感谢大家在这里为止谢谢大家