当我们谈论大规模推荐系统的工程实现时,Twitter 于 2023 年开源的 the-algorithm 提供了一个极为珍贵的参考样本。这个包含数十个微服务与模型的代码库,揭示了日均处理数亿条推文、支撑超过 5 亿用户信息流的推荐系统是如何在工程层面构建的。其中最具代表性的,并非某个深度学习模型,而是名为 GraphJet 的实时图处理引擎 —— 它以一种反直觉的设计选择,实现了每秒百万级边的实时摄入与数万次推荐查询,成为整个推荐流水线的图特征中枢。
单服务器全内存的设计哲学
GraphJet 的核心设计理念可以用一句话概括:假设整个用户 - 推文交互图能够完整载入单台服务器的内存中。这一选择在分布式系统横行的年代显得格外反直觉,但恰恰是 Twitter 团队基于对图增长规律与摩尔定律的深刻洞察做出的务实决策。与其将图切分后引入复杂的跨分片查询与分布式协调开销,不如充分利用单台大内存机器的容量上限,通过简单的副本复制来实现水平扩展。
这种设计背后的数学逻辑值得细究。根据 GraphJet 团队在 VLDB 2016 论文中的估算,一个包含百亿条边的社交图,若采用最朴素的边列表存储,仅需约 80 GB 内存 —— 这在当时的商用服务器上已经完全可行。更关键的是,他们观察到 Twitter 用户社交图的增长速度实际上慢于商用服务器内存容量的增长曲线。这意味着只要每隔几年采购或升级一次服务器硬件,就可以自然地跟随图规模增长,无需频繁重构分布式架构。
从工程实现来看,GraphJet 将整个交互图组织为按时间分区的索引片段(index segments),每个片段内部存储用户节点与推文节点的邻接关系。这种时序分区带来了两个显著优势:一方面,新产生的用户行为可以高效地写入最新的索引片段,避免对历史数据结构的频繁修改;另一方面,当需要查询某个用户最近的交互模式时,只需扫描有限数量的时序片段即可完成,而非遍历整个图。这种设计天然契合推荐系统对实时性的要求 —— 用户刚刚点赞或转发的内容,应当立即影响后续的推荐结果。
幂律分布驱动的存储优化
社交网络的图结构呈现出典型的幂律分布特征:少数超级节点(如热门账号、病毒式传播的推文)拥有极高的度数,而绝大多数普通用户的度数则极低。GraphJet 的存储引擎充分利用了这一统计特性,通过紧凑的边编码与动态内存分配策略实现了显著的存储效率提升。
在边编码层面,GraphJet 针对用户 - 推文二部图的特点采用了定制化的表示方法。由于推文的生命周期有限(大多数推文在发布后数天内的互动占其总互动量的绝大部分),推文侧的边可以采用更紧凑的短期存储结构。而用户侧的边则需要更长期的保留,因为用户的社交关系具有更强的持久性。这种差异化的存储策略避免了为所有节点维护统一但冗余的数据结构。
动态内存分配则针对幂律分布下的度数差异进行优化。对于高度数节点(如拥有百万粉丝的账号),GraphJet 预分配更大的邻接存储空间以避免频繁的内存重分配;对于低度数节点,则采用紧凑的共享池化分配。这种「富者更富」的分配策略与图的自然结构高度吻合,在实验中将存储开销降低了数个百分点 —— 在大规模场景下,这意味着节省数十 GB 乃至上百 GB 的内存占用。
根据论文披露的生产环境数据,单个 GraphJet 服务器能够以每秒 100 万条边的速度持续摄入新产生的用户 - 推文交互,同时在稳态下每秒计算约 500 条推荐结果,折算为每秒数百万次的边读取操作。这一吞吐量在当时的单机图处理系统中属于顶尖水平,也验证了全内存设计在延迟敏感型推荐场景中的优势。
双路召回的工程分工:内网络与外网络
理解 Twitter 推荐系统的召回逻辑,关键在于把握其「内网络」与「外网络」的双路并行架构。For You 时间线的推文来源被严格控制在约 50% 来自用户关注的账号(内网络),另 50% 来自用户未关注的账号但被算法判定为可能感兴趣的内容(外网络)。这种比例划分并非随意设定,而是经过长期 A/B 测试验证的最优平衡点 —— 既要维持用户已有社交关系的黏性,又要不断引入新鲜内容以避免信息茧房。
内网络召回的核心是 RealGraph—— 一个用于预测用户间未来交互概率的有向带权图。在 RealGraph 中,节点代表 Twitter 用户,带权有向边表示两个用户之间发生过某种交互(如关注、回复、点赞、DM),边的权重被建模为未来任意时刻源用户向目标用户发起交互的条件概率。这一建模使得 RealGraph 不仅仅是一个静态的社交关系图,而是一个能够预测未来行为的动态模型。
RealGraph 的特征工程流水线是理解其工作原理的关键。系统首先从用户行为日志中抽取原始交互事件,包括关注关系、推文回复、点赞、点击头像、停留时长等显式与隐式信号。这些原始事件经过聚合与变换后,形成每对用户之间的多维度特征向量,例如「过去 7 天内源用户对目标用户推文的平均点赞概率」「源用户回复目标用户的平均延迟」「双方共同参与的推文数量」等。训练阶段使用逻辑回归模型,以这些特征预测未来是否会发生交互,模型的输出即为 RealGraph 边的权重。
在召回阶段,给定当前用户 u,系统遍历 u 在 RealGraph 中指向的所有用户 v,按照权重排序后选取 top-k 用户,再从这些用户近期的推文中筛选出符合质量与安全过滤条件的候选推文。这一过程本质上是在用户的强关系网络中寻找高质量内容,是「你关注的人发的东西」的算法化增强版本。
外网络召回则由一系列基于协同过滤与内容理解的组件共同完成,其中 GraphJet 扮演核心角色。User Tweet Entity Graph(UTEG)是构建在 GraphJet 之上的用户 - 推文实体图,它维护了用户与推文之间的实时交互关系,并通过图遍历发现用户可能感兴趣但尚未接触的内容。具体而言,UTEG 首先根据用户历史交互的实体(话题、关键词、提及的用户)构建用户的兴趣画像,然后在图上执行受限制的随机游走,从用户节点出发,按照与历史兴趣的相似度概率性地转移到推文节点,最终收集到达的推文作为外网络候选。
另一路重要的外网络召回来自 SimClusters 与 TwHIN 两个嵌入模型。SimClusters 通过社区检测算法将用户划分到数百个稀疏的兴趣社区中,每个社区代表一组具有相似行为模式的用户群体。TwHIN(Twitter Heterogeneous Information Network)则是一个包含 15 亿节点的大规模知识图嵌入模型,将用户、推文、话题等实体映射到统一的潜在空间,使得系统可以通过向量相似度实现跨实体的关联发现。这两路召回的结果与 UTEG 的图遍历结果在 tweet-mixer 层进行融合、去重与初步排序,最终送入重排序阶段。
工程化参数与监控要点
从系统设计的角度,我们可以提炼出若干对构建类似图推荐系统具有参考价值的工程化参数。首先是图规模与服务器内存的配比原则:对于日活用户数在千万至亿量级的社交平台,用户 - 内容交互图的边规模通常在数百亿到千亿级别,对应的全内存存储需求约为 100-200 GB。这一规模在当前 256 GB-512 GB 内存的商用服务器上完全可以容纳,因此单服务器全内存设计在中等规模场景下仍然具有合理性。但若图规模突破数千亿边,则需要考虑分层存储策略 —— 热数据(最近 N 天的交互)保留在内存中,温冷数据(历史交互)则迁移至 SSD 或远程存储。
其次是时序分区的粒度选择。GraphJet 采用按天分区的策略,这一天粒度在实践中被证明是一个合理的平衡点:分区过细会导致索引片段数量爆炸,增加查询时的片段扫描开销;分区过粗则会延长新数据可见的延迟,同时增加无效数据的内存占用。对于需要更低延迟的场景(如实时热点发现),可以引入小时级甚至分钟级的细粒度分区,但这需要在存储开销与查询效率之间做出权衡。
第三是幂律分布下的内存分配策略。一种经验法则是为度数超过均值 K 倍的节点预分配 2K 倍的存储空间,其中 K 通常取 10 到 100 之间的值。这种激进的前置分配虽然会略微增加总体内存占用,但能够显著减少运行时的内存重分配次数与碎片化程度,对吞吐量的稳定性有直接帮助。
在监控层面,图推荐系统需要特别关注三类指标。图新鲜度指标衡量从用户行为发生到该行为反映在推荐结果中的端到端延迟,GraphJet 的设计目标是分钟级的数据可见性。图完整性指标追踪边摄入的成功率与重复率,确保不因数据丢失或重复导致推荐偏差。查询延迟与吞吐量指标则直接反映系统的服务能力,需要在 P50/P95/P99 三个分位上分别监控,确保 99 分位延迟仍在业务可接受范围内。
此外,随机游走参数是影响推荐质量与计算开销的关键变量,包括游走步数、从兴趣节点到内容节点的跳转概率、以及多路召回结果融合时的权重配比。这些参数通常需要通过大规模的离线实验与在线 A/B 测试来调优,而非仅凭理论推导。在 Twitter 的实践中,这些参数每隔数周就会根据用户 engagement 指标的变化趋势进行微调。
Twitter 开源的推荐算法代码库为我们提供了一个完整的图推荐系统工程参考。从 GraphJet 的全内存单服务器设计,到 RealGraph 的特征工程流水线,再到内 / 外网络双路召回的职责划分,每个组件都体现了在大规模场景下对复杂度与性能的务实权衡。这种不追求技术上的「优雅」而追求工程上的「有效」的设计思路,正是生产级推荐系统的核心特征。
资料来源:
- GraphJet VLDB 2016 论文:"GraphJet: Real-Time Content Recommendations at Twitter"
- Twitter the-algorithm GitHub 仓库:组件架构与数据流说明