推荐系统规模化扩展实战:数据、模型与服务全链路解析
做推荐系统的朋友应该都有体会刚开始跑通一个模型不难难的是把它从离线实验搬到全量流量上并且随着用户量、物品量、行为数据的持续增长模型效果和线上性能还能保持稳定可用。我最近花了几周时间集中做了一轮推荐系统 Scaling Up 的技术路线调研从数据规模、模型规模、服务规模三个维度做了梳理也踩了不少坑最后基本摸清了工业界当前的演进方向和主流做法。这篇文章就把这轮调研的完整思路、关键选型和执行细节整理出来给正在做推荐系统扩容、或者打算重构推荐架构的同学做个参考。这篇内容适合两类人一类是刚入行推荐系统想理解规模扩展到底涉及哪些技术栈的同学另一类是已经在线上跑着推荐模型但正被训练时长、资源占用、延迟抖动、效果波动这些问题反复折腾的工程师。我不会画那种看起来很唬人但实际上没什么用的架构总览图尽量把问题拆到具体的参数、方案和排查方法上。1. 先搞清楚推荐系统 Scaling Up 到底在扩什么很多同学一想到规模化第一反应就是加机器、加显存、上分布式。但实际调研下来推荐系统的扩展并不只是资源堆叠它本质上是在回答三个问题数据能不能装下、模型能不能学动、线上能不能扛住。这三个问题分别对应数据规模、模型规模和服务规模三者互相牵制单独扩任何一个都会出问题。1.1 三个维度的扩展缺一不可数据规模的扩展是整个链条的第一步。推荐系统训练依赖用户行为日志、物品属性、上下文特征等海量数据当业务从百万级用户增长到千万级、亿级时样本量会从每天几千万条增长到几十亿条。存储侧要考虑分布式文件系统和特征存储的瓶颈计算侧要考虑样本读取和预处理的速度能否跟上训练节奏。模型规模的扩展是第二步。模型变大通常是为了容纳更多特征、更长的用户行为序列、更细粒度的物品表征。Embedding 层的参数量会随着特征基数的增加而急剧膨胀一个基数过亿的 ID 类特征单 Embedding 就可能占据几个 GB 的内存这直接决定训练参数服务器和在线推理时的内存设计。服务规模的扩展是最后一步也是最容易被低估的。模型训练好只是开始线上要面对的是每秒几千甚至几万次请求每个请求需要在几十毫秒内完成召回、粗排、精排、重排的全链路计算。训练可以容忍分钟级甚至小时级的延迟但线上推理必须做到实时这决定了服务端的架构选型和缓存设计。1.2 扩展背后的核心矛盾调研到后面我发现Scaling Up 真正难的不是单个技术点而是三对矛盾的平衡。第一对是效果与成本的矛盾模型越复杂效果通常越好但训练和推理成本非线性上涨业务上需要明确收益是否匹配投入。第二对是时效与稳定的矛盾增量训练能让模型更快反映用户最新行为但频繁更新也带来线上效果波动和工程复杂度上升。第三对是离线与在线的矛盾离线实验效果很好上线后却因为特征不一致、数据分布差异导致效果回退这是推荐系统规模化过程中最典型的翻车现场。我会在后面的章节里逐一展开这些矛盾对应的技术方案和取舍逻辑。2. 数据侧 Scaling Up样本、特征与负样本的规模化设计数据是推荐系统的燃料规模扩展的第一个瓶颈几乎总是出现在数据侧。我调研时发现很多团队在模型结构和训练框架上花了大把精力却忽略了数据管道本身的扩展能力结果模型一升级数据管道先崩了。数据侧的扩展要重点关注训练样本的组织方式、特征存取的规模和负样本构造策略。2.1 训练样本生成的管道化改造当样本量从百万级增长到十亿级以上离线生成全量训练样本的耗时和资源消耗都会成为瓶颈。常见的做法是把样本生成从批量任务改造成流式管道。用户实时行为经过消息队列进入样本拼接服务与物品特征、上下文特征做实时拼接生成训练样本后直接写入分布式存储同时供增量训练和全量训练使用。我调研的几个开源项目中比较通用的样本格式是 TFRecord 或 Parquet按天分目录存储并按照用户 ID 做哈希分桶这样训练时可以实现更均匀的数据读取。实操中有一个细节容易踩坑样本拼接时的特征版本对齐问题尤其是物品特征可能每天更新但行为日志里记录的是用户发生行为那一刻的物品状态。如果按最新特征拼接会产生一定程度的特征穿越模型离线指标虚高上线后效果明显回退。建议在拼接时使用行为发生时点的特征快照或者接受一个折中方案记录特征版本号以便回溯。特征存储的扩展同样关键。早期可以用 Redis 存全量特征但当特征规模到几百 GB 甚至 TB 级时单一 Redis 实例就不再可行。我见到的比较稳妥的做法是分层存储热特征放本地缓存或 Redis温特征放分布式 KV冷特征放对象存储通过特征平台统一管理版本和发布。这个分层设计需要跟线上的特征获取逻辑打通后面在线推理部分还会细说。2.2 特征工程的规模化从人工到自动化的转变特征规模上去之后人工做特征工程无论从效率还是质量上都难以为继。我在调研中重点关注了自动特征工程方向的进展包括特征交叉的自动搜索、特征选择的规模化方法以及 embedding 化的特征表征。一个比较实用的路线是先用 FMFactorization Machine类模型做特征交叉的初筛把高价值交叉特征找出来再输入深度模型训练。这个做法的好处是计算代价可控同时能让深度模型避免在高维稀疏特征上过度拟合。另一个路线是直接用深层网络学习特征交互让模型自己决定哪些特征组合重要典型如 DCN、AutoInt 这类结构。两者的取舍在于效果增益与训练成本之间的平衡小规模场景用 FM 初筛就够大规模场景往往直接上深度交叉网络并用 MoE 结构做稀疏激活来控制计算量。特征规模化的另一个重点是 embedding 化。对高基数类别特征直接用 one-hot 会让维度爆炸转而使用 embedding 后参数量从特征基数变为 embedding 维度乘以池化大小大幅压缩。但 embedding 表本身也面临扩展问题后续模型侧会展开讲。2.3 负样本的规模化选择随机采样与 hard negative 的搭配负样本是推荐系统里经常被忽视却影响巨大的环节。正样本数量通常远小于负样本而负采样的策略直接影响模型学习到的排序语义。调研中看到的主流做法是以随机采样作为负样本基础这样可以保证样本分布接近真实候选分布在此基础上叠加部分 hard negative比如曝光但未点击的样本、同类别但未交互的物品、其他模型排序靠前但用户未选择的物品来增强模型的判别能力。这块有一个跟规模化直接相关的点负样本的数量选择。扩大负采样比例可以提升模型收敛速度和排序精度但也会线性增加训练样本量和训练时间。工业界常见的做法是负采样比例控制在 1:10 到 1:30 之间并通过估计偏置校正来抵消采样带来的分布偏移。实操中我见过不少团队直接照搬论文里的负采样比例结果在自己数据上效果并不好原因在于数据分布不同负样本难度也不同。建议先在离线小规模数据上做负采样比例的敏感性测试找到适合自己业务的范围再放到全量训练里验证。3. 模型侧 Scaling Up从双塔到多阶段架构的演进模型侧的规模化题目比较复杂它既包括模型结构本身的扩展能力也包括参数量上去之后的训练可行性。我在调研中关注的不是某篇论文里的单一模型结构而是工业界在实际扩展过程中走过的一条有代表性的演进路线。3.1 双塔模型为什么成为召回阶段的主流选择双塔模型在今天几乎是召回阶段的标准配置。它的核心思路是把用户特征和物品特征分别编码成两个向量在线推理时通过向量索引做近邻检索从而在极短时间内从千万级物品中召回候选集。双塔的优势在于 user 塔和 item 塔可以离线预计算尤其 item 塔的向量可以全量 build 索引线上只需计算 user 向量并做 ANN 检索这是它能扛住大规模实时请求的根本原因。但双塔模型有一个被反复讨论的弱点user 和 item 只在最终向量层做交互缺少细粒度的特征交叉导致模型表达能力有限。针对这一点工业界的常见做法不是抛弃双塔而是做增强。比如在 user 塔里引入 multi-head attention 对用户行为序列做聚合在 item 塔里引入更多描述性特征提升物品表示的区分度或者在双塔之后接一个轻量的交叉层做粗排阶段的二次判别。从规模化角度看双塔模型的 embedding 表设计很关键。百万级物品对应百万行 item embedding如果每个 embedding 维度是 64那一张表就是几百 MB加上用户侧特征整体可以控制在单机内存范围内。但如果物品量到亿级单机就扛不住了需要把 embedding 表分片放到参数服务器上这就引出分布式训练和存储的问题。3.2 精排模型的规模化特征交叉与计算效率的权衡精排阶段对单条样本的计算量要求比召回高很多因为精排只在召回的几百个候选上进行但特征交叉的复杂度可以设计得很高。调研中我注意到一个趋势精排模型从早期的 WideDeep逐步演进到 DeepFM、DCNv2、FinalMLP 这类显式特征交叉模型再到引入 Transformer 结构的序列建模模型。这里的取舍逻辑很清晰模型表达能力提升通常伴随着计算量的上升而精排阶段的 latency 预算非常紧张。一个可行的工程方案是划分主模型 辅助模型主模型结构相对复杂但在线可接受辅助模型处理特定目标比如点击率、时长、完播率等然后用多目标学习框架统一训练。这样既保证了整体模型的扩展空间又不会让单一模型的复杂度拖垮在线性能。精排模型规模化的另一个重点是特征接入的宽度。以前大家习惯只接少量强特征后来发现把大量弱特征通过 embedding 方式接入模型效果会有稳定提升。这个做法对训练框架提出了更高的要求特征是百亿级甚至更高基数时embedding 表无法全部放进 GPU 显存必须做特征分片与多卡并行这对参数同步和梯度更新的设计都带来挑战。3.3 用户行为序列的规模化建模用户行为序列是最近几年推荐系统效果提升的重要来源。早期做法是把用户最近点击的几十个物品做 sum pooling 或 attention pooling序列长度受限信息利用不充分。规模化方向上我看到两条主线一条是延长序列长度比如从几十扩展到几百甚至上千另一条是在序列上做更复杂的结构建模比如用 Transformer 编码全部历史行为。序列长度扩展最直接的问题是存储和计算的线性增长以及信息瓶颈。并不是序列越长效果一定越好当序列长度超过某个阈值模型收益会明显边际递减同时训练耗时成倍增加。我在调研中看到的做法是把长序列做两段式建模先用轻量模型对超长序列做摘要产出用户兴趣摘要向量再用主模型基于摘要向量和近期行为做精细化建模。这样既利用长序列信息又控制了主模型的计算复杂度。关于序列建模一个实操经验是注意序列内物品的表征一致性。有的团队在序列特征里直接用物品 ID embedding但序列中的物品在精排模型里又有独立的 embedding两份表征不一致会导致兴趣表达偏移。建议共享底层物品 embedding或用物品 side information 做对齐避免训练过程中出现序列特征与目标特征打架的情况。4. 从训练到上线分布式训练与在线推理的规模化落地模型结构和数据管道都准备好了接下来就是最实际的工程问题怎么把大规模样本和超大 embedding 表高效训练出来以及怎么让模型在线上稳定服务。这部分的调研结论是训练和推理都有相对成熟的框架和方案但真正拉开差距的是工程细节。4.1 分布式训练的框架选型与参数配置当前工业界主流的推荐模型训练框架有 TensorFlow、PyTorch以及针对推荐场景优化的开源方案。我调研的重点是参数服务器架构下的 embedding 扩展。以 TensorFlow 生态来看大规模稀疏模型训练一般会用 embedding 分片加同步或异步训练。embedding 分片的核心是让不同的 worker 负责不同的 embedding 切片通过 PS 或 AllReduce 方式交换梯度。我实测下来稠密参数用 AllReduce 同步更新收敛更稳定稀疏 embedding 用异步更新训练吞吐更高。但这两种方式的混用需要特别注意学习率的设置异步更新下学习率需要适当调低否则容易震荡。一个容易踩坑的地方是 embedding 的维度选择。很多人喜欢把 embedding 维度设成 128 或者 256看起来足够大但副作用是参数量和训练耗时同步上升。我的经验是先在离线用小维度比如 32 维验证特征有效性再逐步放大。实际业务中很多特征 32 到 64 维就已经能表达得很好盲目放大维度带来的收益往往不如多加入几个有效特征。训练资源配置上推荐模型不像大语言模型那样动辄需要上千卡一般几十卡到几百卡就能覆盖很大规模的业务。瓶颈更多出在数据读取和特征拼接环节而不是 GPU 算力。我建议先对数据管道做 profiling确认数据供给是否满足 GPU 利用率再决定是否需要继续加卡。4.2 在线推理的延迟治理与特征服务线上推荐链路一般在几十毫秒内要完成多路召回、粗排、精排和重排。Scaling Up 之后单路召回可能变成五路八路精排模型也更大延迟压力成倍增加。调研中比较有效的治理手段是分层缓存和并行化。分层缓存是解决特征读取瓶颈的主要手段。用户特征、物品特征、上下文特征分别建立缓存缓存命中率达到 90% 以上时特征读取的平均耗时可以从几毫秒降到一个毫秒以内。缓存的更新策略需要跟特征发布节奏对齐避免缓存了过期特征导致线上线下不一致。另一个手段是把无依赖的召回请求并行发起用协程或异步 IO 把多路召回的串行耗时压缩成最大单路耗时这部分收益非常直接。精排模型在在线推理侧的规模控制也很关键。如果精排模型输入特征太多、序列太长单次推理耗时就会超预算。实际操作中常见做法是给精排模型加一个计算量预算比如限制最大序列长度、限制交叉特征数量超出部分做截断或降采样保证最差情况下的延迟也可控。我调研的团队里普遍会用 profiling 工具统计每个算子的耗时占比找到热点再做针对性优化比如算子融合、int8 量化、或者部分特征预计算。4.3 召回与排序的级联架构设计推荐系统规模扩大后全链路通常发展成召回-粗排-精排-重排的级联结构。这个结构不是凭空设计的它的每一步都在控制计算成本的同时保留尽可能多的候选。召回用双塔向量检索从千万物品中筛出几百个粗排用轻量模型在这几百个里筛出几十个精排对这几十个做复杂特征交叉打分重排再结合多样性、新鲜度、业务规则做最终调整。调研中我发现很多团队在粗排环节的设计比较草率直接用精排模型的简化版效果并不好。比较合理的做法是把粗排当成独立的排序任务来优化要保证粗排的排序质量与精排有较高的一致性否则粗排筛掉的东西即使精排认为很好也无济于事。这个一致性可以用粗排和精排的正序率指标来衡量低于某个阈值就说明粗排模型需要单独优化。级联架构的规模化还涉及一个流量分配问题。随着模型版本迭代新模型的 A/B 测试会占用线上流量而流量是有限的尤其小规模场景。这个我后面在常见问题里单独展开。5. 调研中的典型问题与排查技巧实录这一节是我在调研和实际测试过程中遇到的高频问题整理成速查表形式给需要的同学做个参照。每个问题我都尽量给出判断依据和解决方向而不是只给结论。问题现象可能原因排查方向与解决思路离线指标高、线上效果差特征穿越、采样分布不一致检查样本拼接时是否用了未来特征统一离线在线特征来源和版本训练吞吐上不去GPU 利用率低数据管道瓶颈特征拼接耗时高对数据读取做 profiling增加预取和缓存扩大数据分桶数异步训练 loss 震荡不收敛学习率过大embedding 更新不一致调低异步更新的学习率或对稀疏参数做梯度裁剪线上 p99 延迟突刺缓存命中率波动特征存储抖动增加本地缓存容量对特征存储做限流降级热点 key 做本地化模型更新后效果忽高忽低增量数据分布波动模型更新频率不当引入平滑机制比如模型加权平均或延迟上线观察新数据的稳定期双塔召回向量检索结果集中向量分布不均匀部分头部物品占主导对 embedding 向量做归一化和聚类后处理增加 ANN 检索的多样性策略5.1 特征不一致线上线下偏差的根源这个现象我见过太多团队栽跟头。离线训练时用的特征是样本生成那一刻拼接好的在线推理时用的特征是实时从特征服务获取的两边一旦不一致模型学到的映射关系在线上就不成立。最简单的例子某个特征在离线样本里全部是归一化后的值但线上请求时获取的特征值没有做同样的归一化效果差异立刻就能体现出来。排查技巧是建立特征比对流程定期从离线样本和线上请求日志中抽取相同请求的特征做 diff特征不一致的地方会直接显现出来。这个流程做起来有点繁琐但在模型规模扩大后非常值得投入。还有一个容易被忽视的坑是特征生成的时区问题日志按 UTC 存储特征按本地时间生成跨天边界时会产生错位最好统一用同一时区。5.2 数据倾斜训练效率的隐形杀手数据倾斜几乎是大规模推荐训练里必踩的坑。热门物品的样本量可能比长尾物品高出几个数量级训练时这些热点样本会主导梯度更新导致模型对长尾物品的学习严重不足。从训练框架层面看数据倾斜还表现为某些分片上的数据量远大于其他分片拖慢整个训练任务的完成时间。解决数据倾斜的思路有两个方向一个是数据层面做重采样热门物品降采样、长尾物品过采样让训练分布更均衡另一个是模型层面做优化比如对 loss 做样本权重调整或对特征做频率分桶。各团队会根据业务形态选择不同组合。我的经验是先把数据层面的均衡做好模型层面再做细调这样训练过程会更稳定。5.3 模型更新策略全量训练与增量训练的节奏把握规模化之后模型不可能每次都做全量训练太耗时也浪费资源。增量训练可以每天或每小时更新模型但纯增量更新有一个风险时间长了模型会遗忘早期样本导致对低频但重要的模式失去判断力。我调研到的实践做法是增量为主、定期全量的双轨策略。日常用增量训练跟上用户行为变化每两周或每个月做一次全量训练校准同时在增量训练中混入一部分历史采样数据来缓解遗忘。模型上线前还可以做影子评估让新模型在线上以只记录不生效的方式运行一段时间对比新旧模型的决策差异减少直接上线带来的不确定性。6. 最后一轮调研的个人体会这轮调研做下来我最大的感受是推荐系统的 Scaling Up 并没有一条放之四海而皆准的标准答案。不同业务的数据分布、流量规模、团队工程能力都不一样照搬大厂的方案未必能落地但调研过程中总结出来的分析框架是通用的先诊断瓶颈在数据、模型还是服务再针对瓶颈做定向扩展最后用离线评估加线上实验验证效果。另一个体会是扩展过程中最贵的不是机器资源而是排查和定位问题的精力。特征一致性问题、数据倾斜问题、训练稳定性问题每一个都可能在模型规模翻倍之后集中爆发。建议团队在扩容前先建立一套完整的监控和比对体系把问题暴露得越早越能避免后期反复折腾。如果让我给一个比较直接的建议从自己业务最痛的环节入手不要一上来就搞大而全的分布式改造。很多时候把数据管道做稳、特征一致性问题解决掉效果提升比换一个更大的模型要明显得多。推荐系统的扩展是一场持久战先把地基夯实比什么都重要。