设计推荐系统

本文深度设计推荐系统:覆盖召回(协同过滤/向量召回)、粗排与精排、特征工程与特征服务、模型训练与在线推理、近实时特征更新、冷启动问题,以及 AB 实验与离线在线评估,给出完整数据流、特征模型伪代码与量级估算。

推荐系统是消费互联网的「增长引擎」——YouTube 70% 的观看、Netflix 80% 的播放、抖音的沉浸式信息流都来自推荐。推荐系统的架构难点不在单个模型,而在「召回候选 → 特征拼接 → 排序 → 近实时更新 → 实验验证」这一整套工程链路。本文按面试答题结构设计一个内容/商品通用推荐系统。

一句话:推荐系统 = 海量候选里「召回 + 精排」两步漏斗 + 实时的特征管道 + 持续的 AB 实验飞轮,三者缺一不可。

一、需求澄清与量级估算

1.1 需求澄清

  • 推荐什么:信息流(短视频/图文)还是商品?我们做「短视频信息流推荐」为主,可推广到商品。
  • 推荐位:首页信息流下拉无限流,还是固定槽位 + 相关推荐?
  • 实时性要求:用户刚看完一个视频,下一个是否马上受其影响(近实时反馈)?
  • 个性化程度:新老用户策略差异(冷启动)?
  • 实验能力:是否要做 AB 实验平台、离线评估?

明确假设:

需求项假设
内容规模1000 万活跃视频,日新增 10 万
用户规模1 亿注册,5000 万 DAU
请求 QPS峰值 5 万次推荐请求/秒
实时性用户行为 1 分钟内在下次刷新生效
指标时长(watch time)、完播率、互动率、多样性

1.2 量级估算

指标估算值推导
每日推荐请求50 亿次5000 万 DAU × 平均 100 次刷新
每次请求返回20 条一屏内容
召回候选池每请求 1 万条粗筛后进入精排
精排打分5000 万 次/天5 万 QPS × 1 万候选 × 24h 峰值系数
特征写入千万级事件/秒播放、点赞、评论、完播等行为流

一句话:QPS 上十万、特征万亿级,推荐系统必须「分阶段漏斗」——先低成本召回砍到千级候选,再对候选精排,绝不让大模型处理全部候选。

1.3 评价体系与离线指标

推荐系统的目标必须先定义清楚,否则「好不好」无从谈起:

层面指标说明
北极星用户时长 / GMV / 留存业务最终目标
核心体验点击率、完播率、互动率内容质量与相关性
多样与健康类目覆盖率、长尾曝光、信息茧房度防同质化
系统质量延迟、资源成本工程约束

离线指标(AUC/GAUC/NDCG)评估「模型好不好」,在线指标评估「对业务有没有用」,两者都要。面试能说出「北极星 → 体验 → 多样 → 成本」的指标分层,就展示了业务 sense。

二、高层架构设计

用户行为(App / Web 埋点)                        内容库(视频/商品)
     │                                              │
     ▼                                              ▼
┌─────────────────────────────────────────────────────────────┐
│                      数据与特征平台                             │
│  行为日志Kafka ─▶ Flink 实时特征 / 离线数仓特征 ─▶ 特征存储     │
│  内容属性、用户画像、社交关系、序列特征                        │
└───────────────┬─────────────────────────────────────────────┘
                ▼
┌─────────────────────────────────────────────────────────────┐
│                      推荐服务(在线链路)                      │
│  请求 ─▶ 召回(多路) ─▶ 粗排 ─▶ 精排 ─▶ 重排(多样性/打散) ─▶ 响应│
│  召回路: 向量相似(ANN) / 协同过滤 / 热门 / 语义 / 运营池         │
└───────────────┬─────────────────────────────────────────────┘
                │ (精排打分需要模型)
┌───────────────▼─────────────────────────────────────────────┐
│                  模型训练与实验平台                            │
│  样本日志 ─▶ 特征拼接 ─▶ 离线训练(GBDT/DeepFM/Transformer)      │
│  ─▶ 模型发布/灰度 ─▶ AB 实验系统 ─▶ 指标看板(时长/互动/多样性)     │
└─────────────────────────────────────────────────────────────┘

三大子系统:

  1. 数据与特征平台:负责行为埋点、特征计算与存储,支撑在线和离线。
  2. 在线推荐服务:请求漏斗(召回→粗排→精排→重排),低延迟返回。
  3. 训练与实验平台:离线训练、模型上线、AB 验证,形成「数据飞轮」。

2.1 为什么召回和精排必须分开

  • 候选量大:1000 万内容不可能逐个跑大模型,先召回砍到 1000-10000。
  • 计算成本差异:召回可以用向量索引、倒排、协同过滤等轻量方法;精排用复杂模型(DeepFM、双塔 + 交叉)。
  • 各司其职:召回负责「找对方向」(recall 高),精排负责「排准顺序」(precision 高)。

一句话:召回保证「不漏」,精排保证「不错」,两段漏斗让「亿级候选」变成「可被模型逐条打分」的百级列表。

三、核心组件设计

3.1 召回层(多路召回)

召回不是单一方法,而是多路并行、结果合并去重:

召回路原理适合成本
向量召回(ANN)用户/物品 Embedding 做近似最近邻大规模内容,主召回高(需建索引)
协同过滤(ItemCF/UserCF)行为相似物品/用户中长尾、关系网中
语义召回文本 Embedding(标题/标签)冷启动、跨域中
热门召回全局热门/区域热门兜底、新用户低
运营池召回人工精选内容活动、调性低
序列召回(Next-It-Net)用户行为序列预测下一个时效性强内容高

向量召回架构:

物品侧 Embedding(离线产出) ─▶ 向量索引(Faiss / HNSW) ─▶ ANN Top-K
用户侧 Embedding(在线拼接实时特征) ─▶ 查询向量
  → 返回 K 个候选(如 200 路 × 30 个)

3.2 粗排与精排

  • 粗排:轻量模型(双塔打分、LR、GBDT 小模型)在 1000-10000 候选上快速打分,筛出 100-300 条进精排。目标是控制精排计算量。
  • 精排:复杂模型(DeepFM / DIN / 多任务 MMoE)对 100-300 条精确打分。每条候选要拼接特征、过模型,成本高但准。
def rank_pipeline(user_id, candidates):
    # 1. 召回合并去重
    recalls = merge(multi_recall(user_id))          # ~10000
    # 2. 粗排: 轻量模型过滤
    mid = coarse_rank(user_id, recalls, limit=300)  # ~300
    # 3. 精排: 复杂模型打分
    scored = [(c, fine_model.score(user_feat, item_feat(c))) for c in mid]
    # 4. 重排: 多样性/打散/去重同质内容
    return rerank(sorted(scored, reverse=True), limit=20)

3.3 特征工程与特征服务

特征分三类:

  1. 用户特征:性别年龄、城市、注册时长、历史兴趣向量、近 7 天行为聚合。
  2. 物品特征:类目、标签、发布时间、完播率/点击率统计、物品 Embedding。
  3. 上下文特征:时间(早中晚)、设备、网络、场景(首页/搜索/相关推荐)。

特征服务(在线拼接):特征计算后统一写入特征存储(Redis / FeatHub / TeraSort 风格),在线推荐服务一次取回全量特征向量,避免逐特征 RPC。

实时特征流: 行为事件 Kafka ─▶ Flink 窗口聚合 ─▶ 特征存储(Redis, TTL 7天)
离线特征: 数仓 T+1 ─▶ 特征表 ─▶ 同步到在线存储

一句话:特征服务是「在线模型的地基」,要把特征做成「预计算 + 存储 + 一次性取回」,而不是在线实时算每个特征。

3.4 重排与多样性控制

精排给出的是「单条候选的预测价值」,但用户要看的是一整屏,必须做重排:

重排手段解决的问题实现
同源打散连续 N 条来自同一召回源/同一作者贪心插入:选价值最高且与前 K 条不冲突的
类目均衡避免信息茧房、类目比例失衡按用户兴趣分布的类目配额
时效加权新内容/热点优先精排分数上叠加时间衰减
运营混排置顶活动、品牌合作插槽规则,保底位置

重排本质是「带约束的序列优化」,贪心即可(近似最优),面试能讲清「精排看单条、重排看整屏」的分工就够了。

一句话:精排负责「每条值多少分」,重排负责「整屏怎么摆才不腻」,两者目标不同,缺一不可。

3.5 特征拼接与推理

在线推理把「用户特征 + 物品特征 + 上下文」拼成模型输入,注意三点:

  1. 一次性取回:按 user_id + 候选 item_id 批量从特征服务取回,避免 N 次 RPC。
  2. 特征版本一致性:离线训练与在线推理用同一版本的特征 schema,否则「训练偏差」让模型离线好、线上崩。
  3. 缓存热点:高热度物品的特征可长时间缓存,用户特征按活跃度分层缓存(活跃用户 TTL 短、普通用户 TTL 长)。
def assemble_features(user_id, items, ctx):
    user_feat = feat_store.get_user(user_id, ctx)
    item_feats = feat_store.batch_get_items(items)      # 批量
    return [concat(user_feat, item_feats[i], ctx) for i in items]

四、数据模型

样本与特征的核心表(离线):

CREATE TABLE train_sample (
  sample_id     BIGINT,
  user_id       BIGINT,
  item_id       BIGINT,
  label         TINYINT,       -- 是否点击/完播/时长分桶
  context       VARCHAR(64),   -- 场景+位置
  features      MAP<STRING,FLOAT>, -- 特征快照(命中时拼接)
  date          STRING,        -- 分区
  KEY (user_id, date), KEY (item_id)
) PARTITIONED BY (date);

CREATE TABLE user_embedding (
  user_id     BIGINT PRIMARY KEY,
  embedding   ARRAY<FLOAT>,    -- 128/256 维
  version     INT,
  updated_at  DATETIME
);

CREATE TABLE item_embedding (
  item_id     BIGINT PRIMARY KEY,
  embedding   ARRAY<FLOAT>,
  category    INT,
  hot_score   DOUBLE,
  status      TINYINT
);

在线特征存储用 Redis/KV:rec:user:{uid} → 用户特征 JSON;rec:item:{iid} → 物品特征。KV 大表用一致性哈希分片。

五、关键流程

5.1 一次推荐请求时序(在线链路)

用户刷新 → 网关鉴权 → 推荐服务
 1. 组装上下文特征(设备/时间/场景)
 2. 取用户特征(Redis) + 用户实时行为序列(最近100条)
 3. 多路召回并行 → 合并去重(约1万)
 4. 粗排(轻量模型) → 300
 5. 精排(复杂模型逐条打分) → 排序
 6. 重排(打散同源/避免连刷/运营混排) → 20条
 7. 拼装物品摘要/封面 → 返回(延迟要求 < 200ms)

延迟预算:召回 40ms + 特征拼接 30ms + 粗排 20ms + 精排 80ms + 重排 10ms ≈ 180ms,各环节并行化。

5.2 近实时特征更新(数据飞轮)

用户点击视频 A(1s内) → 埋点 → Kafka → Flink 实时特征
  → 更新用户序列特征(Redis) / 实时 CTR 计数
  → 1分钟后用户再次刷新: 新特征已生效,推荐结果紧跟行为
  → 行为日志离线落数仓 → T+1 样本 → 训练新模型 → 发布 → 影响后续推荐

一句话:近实时更新靠「Flink 实时特征 + Redis 缓存生效」支撑分钟级反馈,离线再训练支撑天级模型演进,这就是数据飞轮。

5.3 模型训练与在线推理

离线训练流水线:

行为日志 → 特征拼接(join 特征表) → 样本采样/负样本构造 → 训练(DeepFM/DIN/MMoE)
  → 模型评估(离线指标) → 模型注册中心 → 发布

在线推理两种形态:

形态适用说明
近线批量打分粗排、热门榜定期(分钟/小时)对候选池打分写入 KV
在线实时打分精排请求到达时对 100-300 候选逐条打分

模型发布用「灰度 + 回滚」:新模型先小流量(如 1%),AB 指标正向再逐步放量;异常自动回滚到旧版本。

一句话:训练与推理要「特征同构、版本可控、灰度发布」,否则模型迭代就是在线上裸奔。

5.4 用户画像构建

用户画像是「特征工程 + 知识沉淀」的结合体:

用户画像 = 静态属性(注册/设备/地域) 
        + 短期行为(最近N次点击/观看序列, 实时更新)
        + 长期兴趣(类目/标签权重衰减, 半衰期模型)
        + 关系图谱(社交关注、相似用户)
        + 向量化(行为序列 → 用户 Embedding, 周期性重算)

画像的存储与更新:

  • 静态画像:MySQL / 数仓 T+1。
  • 实时画像:Redis(KV,TTL 控制);序列特征用 Redis List(保留最近 100 条行为)。
  • 用户 Embedding:离线周期性计算 + 增量更新,向量库存。

一句话:画像分「静态 + 短期 + 长期」三桶,短期走实时、长期走离线,各有各的存储与更新频率。

5.5 场景化推荐差异

不同推荐位对链路的要求不同:

场景候选池延迟要求主要链路
首页信息流全量200ms多路召回 + 粗排 + 精排 + 重排
相关推荐(详情页)当前物品邻居50ms向量召回 + 轻量排序
搜索后推荐当前 query 相关100ms语义召回 + 排序
运营 banner运营池即时规则/权重排序

面试答出「场景决定链路深浅」比「一套方案打天下」更有经验含量。

六、冷启动与 AB 实验

6.1 冷启动问题

  • 新用户:无行为 → 用热门召回 + 地域/设备画像 + 引导式选择兴趣标签;逐步积累行为后过渡到个性化。
  • 新物品:无曝光 → 用语义召回(文本/封面 Embedding)补充曝光;探索机制(bandit / 一定比例探索流量)让新内容有机会被看见。
冷启动对象数据来源策略
新用户注册信息、设备、地域热门 + 热门类目 + 兴趣引导
新物品内容属性、标题、标签语义 Embedding + 探索曝光
新场景/活动运营池强运营干预 + 规则召回

6.2 评估与 AB 实验

离线评估:GAUC、AUC、NDCG@K、召回率/精确率;防止离线指标与线上不一致(偏差)。
在线 AB:流量分桶(按 user_id hash),对比时长、完播率、互动率、多样性,做显著性检验(t 检验、AA 回归)。

实验平台: 实验配置中心 → 分流(SDK) → 埋点回流 → 指标看板 → 决策(放量/回滚)

七、性能与扩展

  • 向量索引:HNSW/IVF-PQ 在内存(每亿向量约几十 GB),分片多副本承载高 QPS。
  • 缓存:热门结果/热门用户特征缓存;用户请求结果缓存 TTL 数秒,防抖刷。
  • 降级:精排模型超时降级到粗排结果;召回超时丢路;兜底热门列表。
  • 弹性:大促/晚高峰扩容,模型版本灰度。

成本与扩展权衡

手段收益代价
增加召回路recall 提升合并去重成本、延迟增加
精排模型更复杂排序精度延迟、GPU/CPU 成本翻倍
特征更实时反馈更及时Flink 资源、存储成本
更大候选池长尾覆盖精排计算量线性上涨

八、权衡与备选

决策点本文选型备选权衡
向量索引Faiss(HNSW)Milvus / VespaFaiss 自建可控;Milvus 托管省心
特征流Kafka + FlinkSpark Streaming / 自研Flink 低延迟窗口;Spark 批处理更省资源
精排模型DeepFM + MMoE双塔、Transformer、重排 GNN精度 vs 延迟/成本折中
召回多路并行合并单一向量召回多路更稳,但需合并逻辑
实时性分钟级 Flink秒级 lambda 架构秒级成本高,收益边际递减

取舍原则

  • 延迟 > 极致个性化:信息流 200ms 内出结果,模型不能太慢。
  • 多样性与相关性平衡:高相关但同质会让用户腻,加打散和多样性指标。
  • 离线与在线指标一致:做不好评估,模型演进就是盲人摸象。

九、扩展场景与面试追问

9.1 相关推荐 / 详情页推荐

详情页「看了又看」「买了又买」是独立场景:不依赖用户全局画像,只看「当前物品 → 相似物品」(ItemCF 或物品向量最近邻),延迟要求更高(毫秒级),候选池小(单物品邻居),可以走纯向量召回 + 规则重排。

9.2 商品推荐 vs 内容推荐差异

维度内容(视频/图文)商品
反馈信号完播、时长、点赞点击、加购、转化、GMV
目标时长/留存转化率/客单价
候选池变化日增 10 万新内容相对静态,长尾多
冷启动新内容靠语义曝光新商品靠类目 + 相似品

9.3 多目标优化与算力约束

推荐要同时优化「时长、互动、多样性」,常见做法:

  • 多目标模型(MMoE/PLE):共享底层表示、各目标独立 tower,用 gate 加权融合。
  • 目标权重调参:线上用「时长/互动/多样性」加权分排序,权重靠实验调。
  • 算力约束:精排 GPU 批推理、粗排用轻量模型、候选池上限设置——把算力花在「最可能被点」的候选上。

9.4 面试常见追问

追问关键回答
用户画像怎么做?注册属性 + 行为聚合(类目/标签加权)+ 向量化,离线 T+1 + 实时增量
负样本怎么构造?曝光未点击(in-batch 负采样)+ 随机采样,注意防「曝光偏差」
推荐结果总是同一类怎么办?重排打散 + 多样性指标进 AB 评估,必要时限制类目占比
新内容没人看怎么办?语义召回 + 探索流量(一定比例随机/bandit 曝光)+ 冷启动扶持池
模型在线延迟太高?特征预计算、批量取回、近线打分、小模型蒸馏、GPU 批推理

十、总结

模块关键点一句话记忆
召回多路并行 + 向量 ANN先找对方向,成本低
粗排/精排两段漏斗粗排砍量、精排保质
特征服务预计算 + 存储 + 一次性取回特征地基决定模型上限
近实时Kafka + Flink + Redis分钟级反馈生效
冷启动热门 + 语义 + 探索无数据也能推荐
实验AB 分流 + 指标评估数据飞轮验证每次迭代

一句话:推荐系统面试先讲「召回→精排→重排」漏斗和「离线/在线一致性」,再深入特征服务与近实时更新,最后用冷启动与 AB 实验展示工程闭环——这比背一个模型更有区分度。

继续阅读

探索更多技术文章

浏览归档,发现更多关于系统设计、工具链和工程实践的内容。

全部文章 返回首页

「design」更多文章

  1. 设计一个消息队列系统(类 Kafka)
  2. 设计日志与监控系统
  3. 设计搜索引擎