← 2026-05-17 📂 All Days 2026-05-20 →

🏗️ 系统设计 / System Design — 推荐系统 / Design a Recommendation System

📅 Day 46 | ⏱️ 3 min read | 🔴 Advanced

想象你在设计... / Imagine You're Designing...

你加入了一家类似 Netflix 的公司,PM 找你说:"用户流失率太高了,我们要给每个用户推荐他们真正想看的内容。" 每天有 2 亿活跃用户,内容库有 1500 万个视频。你怎么设计?

You join a Netflix-like company. The PM says: "Churn is too high — we need to recommend content users actually want to watch." 200M DAU, 15M videos in the catalog. How do you design this?


🏛️ Architecture Diagram

User Request │ ▼ ┌─────────────────┐ │ API Gateway │ └────────┬────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ Recommendation Service │ │ │ │ ┌─────────────┐ ┌──────────────────┐ │ │ │ Candidate │ │ Ranking Layer │ │ │ │ Generation │───▶│ (ML Ranker + │ │ │ │ │ │ Business Rules)│ │ │ └─────────────┘ └──────────────────┘ │ │ │ │ │ │ ┌────▼────┐ ┌──────▼─────┐ │ │ │ Multiple│ │ Feature │ │ │ │ Sources │ │ Store │ │ │ └─────────┘ └────────────┘ │ └─────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ Candidate Sources │ │ │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ Collaborative│ │Content-Based │ │ │ │ Filtering │ │Filtering │ │ │ │ (user-item │ │(item embeddings) │ │ │ │ matrix) │ │ │ │ │ └──────────────┘ └──────────────────┘ │ │ │ │ ┌──────────────┐ ┌──────────────────┐ │ │ │ Trending │ │ Contextual │ │ │ │ / Popular │ │ (time, device, │ │ │ │ │ │ location) │ │ │ └──────────────┘ └──────────────────┘ │ └─────────────────────────────────────────────┘ │ ▼ ┌─────────────────────────────────────────────┐ │ Data Infrastructure │ │ │ │ User Events ──▶ Kafka ──▶ Feature Store │ │ (clicks, watch │ (Redis online │ │ time, skips) │ features) │ │ │ │ │ ▼ │ │ Spark / Flink │ │ (batch + stream) │ │ │ │ │ ▼ │ │ Embedding Store (FAISS/ │ │ Pinecone for ANN search) │ └─────────────────────────────────────────────┘

🔑 关键设计决策 / Key Design Decisions

两阶段架构 / Two-Stage Architecture

推荐系统的标准做法是候选生成 → 精排,这不是偶然的:

The industry-standard two-stage approach—candidate generation → ranking—exists for good reason:

StageInputOutputLatency Budget
Candidate GenerationUser ID100-500 items50ms
Ranking500 items + featuresTop 20100ms
Business FiltersTop 20Final list10ms

候选生成 追求召回(recall),用轻量模型快速从百万候选里找到相关的。

精排 追求精度(precision),用复杂模型在少量候选里找最好的。

协同过滤 vs 内容过滤 / Collaborative vs Content-Based

Collaborative Filtering: "和你相似的用户喜欢 X" User A watched: [Movie1, Movie2, Movie3] User B watched: [Movie1, Movie2, ?] → Recommend Movie3 to User B Content-Based: "你看过的东西和 X 相似" User watched: Action movies with Tom Hanks → Recommend similar action/Tom Hanks movies Hybrid (Netflix/Spotify approach): Combine both + context signals

冷启动问题 / Cold Start Problem

场景策略
新用户推热门内容 + 引导填写偏好
新内容用内容特征(genres, actors)+ 曝光池
新平台先热门,再逐渐收集数据

⚖️ 关键权衡 / Key Tradeoffs

实时 vs 批量特征 / Real-time vs Batch Features

批量 (Hadoop/Spark, 每小时更新):
✅ 历史偏好、长期兴趣模型
❌ 无法捕捉用户刚才的行为

实时 (Flink, 秒级更新):
✅ 当前 session 的行为(刚搜了什么)
❌ 计算昂贵、延迟要求高

实践中:Lambda 架构 — 批量做基础,实时做修正。

精确推荐 vs 多样性 / Accuracy vs Diversity

如果只推用户最喜欢的类型,他们会陷入信息茧房。Netflix 有明确的 diversity budget:每20个推荐里,强制包含至少3个用户不常看的类型。


☠️ 别踩这个坑 / Common Mistakes

坑1:忘记 Feedback Loop(反馈循环)

推荐系统会自我强化。只推热门 → 热门更热门 → 新内容永远没机会。必须加 **exploration factor**(比如 ε-greedy 或 UCB)。

坑2:混淆 Offline 和 Online 指标

离线 A/B 测试 CTR 很好,不代表线上用户满意。Netflix 发现,用户满意度更好的指标是 watch time after click,而不是 CTR 本身。

坑3:忽略延迟

推荐要在 <200ms 内返回(含网络)。重模型必须离线预计算,用 cache 存结果。


🧒 ELI5

推荐系统就像一个超级聪明的图书馆员。她记得你借过什么书,知道和你品味相似的人喜欢什么,还会把最新上架的热门书放在你面前——但不会每次都给你一样的书,不然你会无聊的!

A recommendation system is like a super-smart librarian. She remembers what you've borrowed, knows what people with similar tastes enjoy, and shows you popular new books — but she makes sure to occasionally surprise you so you don't get stuck in a rut.


📚 References

💻 算法 / Algorithms — #19 Remove Nth Node From End of List (Medium)

📅 Day 46 | ⏱️ 4 min read | 🟡 Medium | 🧩 Linked List (5/11)

🧩 链表技巧模式 (5/11) — 延续 Day 41 开始的模式

今天是链表模式的第5题。前4题:#206 Reverse → #21 Merge → #141 Cycle → #143 Reorder
Today is the 5th problem in the Linked List block. You already have: reverse, merge, cycle detection, reorder.

今天的新变化 / What's new today:

前4题主要用快慢指针找中点或检测环。今天用快慢指针的变体 — 用固定间距的双指针找"倒数第 N 个节点"。核心洞察从"speed ratio"变成"distance gap"。


🔗 Links


🌍 Real-World Analogy

你在一个很长的火车上。列车长想删掉从车尾数第 N 节车厢,但他在车头,看不到车尾,也不知道火车总长度。他只能从车头往车尾走一次。

怎么做?他派出两个列车员:一号先走 N 步,然后两人同时往前走。当一号到终点时,二号正好站在目标车厢前面。

You're on a very long train. The conductor wants to remove the Nth car from the tail but is standing at the head with no view of the total length. Solution: two conductors — one walks N steps ahead, then both walk in sync until the first reaches the end. The second is now right behind the target car.


🎯 Problem

Given a linked list, remove the nth node from the end. Return the head of the modified list. Example: 1 → 2 → 3 → 4 → 5, n=2 ↑ remove this Result: 1 → 2 → 3 → 5

关键约束: One pass preferred. n is always valid.


🧩 模板映射 / Mapping to Pattern Template

标准快慢指针模板是 fast.next.next(速度比 2:1 找中点)。

今天的变体:fixed-gap two pointers(固定间距,速度相同)。

Template (from Day 41): fast moves 2x, slow moves 1x → finds middle Today's variation: fast starts N steps ahead, then both move 1x → fast at end means slow is N steps from end

这个变体你会在以下场景反复用到:

  • 找倒数第 K 个节点(本题)
  • 找链表后 1/3 处的节点
  • 任何"相对位置"问题

🐍 Python Solution

from typing import Optional

class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def removeNthFromEnd(head: Optional[ListNode], n: int) -> Optional[ListNode]:
    # Dummy node handles edge case: removing the head itself
    dummy = ListNode(0, head)
    
    fast = dummy
    slow = dummy
    
    # Advance fast by n+1 steps (not n) so slow lands on the node BEFORE target
    # Why n+1? We need slow to be the predecessor for the delete operation
    for _ in range(n + 1):
        fast = fast.next
    
    # Move both until fast reaches end
    while fast:
        fast = fast.next
        slow = slow.next
    
    # slow.next is the node to delete
    slow.next = slow.next.next
    
    return dummy.next

# --- Trace for [1,2,3,4,5], n=2 ---
# After setup:
#   dummy -> 1 -> 2 -> 3 -> 4 -> 5 -> None
#   fast, slow both at dummy

# After n+1=3 steps, fast is at node 3:
#   fast = 3, slow = dummy

# While loop (fast moves until None):
#   fast=4, slow=1
#   fast=5, slow=2  
#   fast=None, slow=3  ← STOP

# slow is at node 3, which is one BEFORE the target (node 4)
# slow.next = slow.next.next → skip node 4
# Result: 1 -> 2 -> 3 -> 5 ✓

为什么 n+1 不是 n?

删除操作需要修改 predecessor.next,所以 slow 必须停在目标节点的前一个,因此 fast 要多走1步。


⏱️ Complexity

TimeSpace
This solutionO(n)O(1)
Naive (2 passes)O(n)O(1)
Stack-basedO(n)O(n)

一次遍历,常数空间 — 这是最优解。


🔄 举一反三 / Pattern Connections

问题技巧与今天的关系
#141 Linked List Cycle (Day 43)fast=2x, slow=1x 检测环同样双指针,不同速度比
#143 Reorder List (Day 44)fast=2x 找中点速度变体
今天 #19fast 领先 N 步找倒数第 N间距变体
#876 Middle of Linked Listfast=2x 找中点最基础的快慢指针

规律: 链表的双指针解法,调整的只有两个参数:速度比(2:1 vs 1:1)和初始间距(0 vs N)。


📝 Quiz

{"question":"To remove the Nth node from the end in ONE pass, where should 'slow' pointer stop?","options":["Exactly at the Nth node from end","At the node BEFORE the Nth node from end","At the Nth node from start","At the middle of the list"],"correct_index":1}

🧒 ELI5

想象你有一根绳子,但不知道它有多长。你想剪掉从末尾数第2段。怎么办?用两根手指:右手先走2步,然后两手同时往前走,右手碰到尽头时,左手就在要剪的地方旁边了!

Imagine a rope of unknown length. You want to cut the 2nd segment from the end. Use two fingers: right hand walks 2 steps ahead, then both move together. When the right hand hits the end, the left hand is right next to the cut point!


📚 References

🗣️ 软技能 / Soft Skills — 如何评估和采纳新技术?

📅 Day 46 | ⏱️ 2 min read | 🔴 Senior/Staff | Category: Technical Vision

❓ 问题 / The Question

"How do you evaluate and adopt new technologies for your team?"

这道题考的不是你是否跟得上技术趋势,而是你是否有系统性的判断框架,以及是否能为团队的技术决策承担责任。

This question isn't about whether you keep up with trends — it's about whether you have a systematic framework for judgment and whether you can own the consequences of your team's tech decisions.


🌟 为什么这很重要 / Why This Matters

作为 Senior/Staff,你的技术选型决策会影响整个团队未来2-3年的工作。一个被炒作带偏的决策("大家都在用 GraphQL!")可能让团队陷入维护噩梦。一个过于保守的决策则会让团队错失生产力提升。

Your tech adoption decisions shape how the team works for years. Hype-driven choices create maintenance nightmares; over-conservatism leaves productivity gains on the table.


⭐ STAR 拆解 / STAR Breakdown

Situation

"我们的数据管道用的是一套自研的 batch 系统,处理延迟达到 4 小时。团队里有人提议切到 Apache Flink 做实时流处理。"

"Our data pipeline used an in-house batch system with 4-hour lag. A team member proposed migrating to Apache Flink for real-time streaming."

Task

"作为 tech lead,我需要评估这是一个真正的技术提升,还是为了技术而技术的冲动。"

"As tech lead, I needed to evaluate whether this was a genuine improvement or technology for its own sake."

Action

我用了一个四步框架 / I used a four-step framework:

  1. 问题优先 / Problem First:先问"我们真正要解决的问题是什么?"是延迟?吞吐量?可维护性?

Start with: "What problem are we actually solving?" Latency? Throughput? Maintainability?

  1. 成本全核算 / Total Cost of Ownership:不只是性能,还有学习曲线、招聘难度、运维复杂度、迁移成本。

Not just performance gains — learning curve, hiring difficulty, operational complexity, migration cost.

  1. 低风险验证 / Low-Risk Validation:先在一个非关键管道上做 spike(时间盒限定1周),不上生产。

Time-box a spike (1 week) on a non-critical pipeline. No production risk.

  1. 退出条件 / Exit Criteria:事先定好"什么情况下我们会说不",而不是做了一半骑虎难下。

Define upfront: "Under what conditions do we walk away?" Don't let sunk cost drive decisions.

Result

"Spike 验证了 Flink 确实能解决延迟问题,但我们也发现 Kafka Connect + simple stream processors 能以 20% 的复杂度实现 80% 的收益。我们最终采用了简化方案,3 个月后延迟从 4h 降到了 10 分钟,没有新的运维负担。"

"The spike confirmed Flink solved the latency problem, but we also found Kafka Connect + simple stream processors could get 80% of the benefit with 20% the complexity. We shipped the simpler solution — latency went from 4h to 10min with no new operational overhead."


❌ Bad vs ✅ Good

❌ "我们用 Kubernetes 因为所有人都在用。"

→ 这是 FOMO 驱动的决策,没有与业务问题挂钩。

✅ "我们评估了 Kubernetes,发现它解决的问题我们没有(<10 个服务,单机可以运行),所以推迟到规模扩大再考虑。"

→ 这显示了技术判断,而不是盲目跟风。


💎 Senior/Staff 加分项 / Senior/Staff Tips

  • 提到技术债务权衡:新技术引入也是在创造新的技术债务,要明确说出来
  • 提到团队知识分布:如果只有一个人懂新技术,这是风险,不是优势
  • 提到可逆性:好的技术决策应该尽量可逆(先用 feature flag,先做小模块)
  • 说出你曾经拒绝采纳某个热门技术的经历,比"我推动了某某技术"更有说服力

🎯 Key Takeaways

  1. 问题驱动,不是技术驱动 — Problem first, technology second
  2. 全成本核算 — Performance is easy; operational cost is hard
  3. 时间盒验证 — Spike before committing
  4. 事先定好退出条件 — Define failure criteria upfront

🧒 ELI5

就像买车。不能因为邻居买了特斯拉你就买。先问自己:我主要在城里开还是跑长途?我有充电桩吗?我的预算是多少?答案对了,再选车型。

It's like buying a car. Don't buy a Tesla just because your neighbor did. First ask: mostly city driving or highway? Do I have a charger? What's my budget? Answer those first, then pick the model.


📚 References

🤖 AI — 推理优化:KV Cache 与 Speculative Decoding

📅 Day 46 | ⏱️ 2 min read | 🔴 Mastery | Category: Deployment

🎯 为什么这很重要 / Why This Matters

你每次用 ChatGPT、Claude 或 Gemini,背后的推理成本都是实实在在的钱和能源。据业界估计,2025-2026 年,AI 推理已占 AI 算力总支出的 70-90%。让推理更快、更省,是 AI 工程最热门的方向之一。

Every ChatGPT or Claude response costs real money and energy. By some industry estimates, AI inference accounts for 70-90% of total AI compute spending in 2025-2026. Making inference faster and cheaper is one of the hottest areas in AI engineering.


🔑 概念1:KV Cache — 记忆力优化 / Memory Optimization

问题 / The Problem

Transformer 生成每个 token 时,需要"关注"(attention)所有之前的 token。如果不缓存,每生成一个新词都要重新计算所有前面词的 Key 和 Value 矩阵,成本是 O(n²)。

Without caching, generating each new token requires recomputing Key and Value matrices for ALL previous tokens — O(n²) cost.

KV Cache 是什么 / What It Is

Without KV Cache: Token 1 generated → compute K,V for [token1] Token 2 generated → recompute K,V for [token1, token2] ← wasteful! Token 3 generated → recompute K,V for [token1, token2, token3] With KV Cache: Token 1 generated → compute K,V for [token1], cache it Token 2 generated → reuse cached K,V for token1, compute only token2's Token 3 generated → reuse cached K,V for token1+2, compute only token3's

效果: 推理速度大幅提升,但代价是 GPU 显存。在 1M token 的上下文下,KV Cache 可以占用 GPU VRAM 的 70-90%。

2025-2026 的新进展 / Recent Advances

  • PagedAttention (vLLM):把 KV Cache 当虚拟内存管理,减少约 55% 内存碎片,同等显存处理 2x 并发请求
  • 量化 KV Cache:用 FP8 而不是 FP16 存储,内存减半,精度损失 <1%
  • Prefix Caching:相同 system prompt 的多轮对话,KV 直接复用,延迟大幅降低

🔑 概念2:Speculative Decoding — 并行猜测 / Parallel Guessing

问题 / The Problem

大模型(如 70B 参数)生成 token 的速度很慢——每次只能生成一个 token,是串行的。

Large models generate one token at a time — it's inherently sequential and slow.

Speculative Decoding 的思路 / The Idea

Normal Autoregressive Decoding: Large Model → token1 → token2 → token3 → token4 ... (slow) (slow) (slow) (slow) Speculative Decoding: Small "draft" model → quickly guesses: [token1, token2, token3, token4] Large "target" model → verifies ALL 4 in parallel! If accepted: 4 tokens generated at the cost of ~1 verification step ✓ If rejected at token3: discard token3+4, continue from there Net effect: 2-3x throughput improvement for tasks with predictable output

关键洞察: 小模型(如 7B)大部分时候猜得对(尤其是代码生成、补全任务)。大模型的并行验证比串行生成快得多。

现实中的应用 / Real-World Usage

  • Google 在 Gemini 推理中使用 speculative decoding(据报道)
  • Anthropic Claude 的 extended thinking 优化中涉及类似技术
  • vLLM 开源框架支持 speculative decoding
  • 2025 Intel + Weizmann Institute:新方法让任意小模型都能加速任意大模型,无需词汇表匹配,据报道可达 2.8x 加速

Source: Intel Newsroom


🐍 可运行代码 / Runnable Code

# pip install transformers torch
# Conceptual demo of KV Cache behavior in HuggingFace

from transformers import AutoTokenizer, AutoModelForCausalLM
import torch
import time

model_name = "gpt2"  # Small model for demo
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)

prompt = "The key insight about caching is"
inputs = tokenizer(prompt, return_tensors="pt")

# With KV cache (default) — model.generate() uses KV cache automatically
start = time.time()
output_with_cache = model.generate(
    **inputs,
    max_new_tokens=30,
    use_cache=True  # default: True
)
print(f"With KV cache: {time.time()-start:.3f}s")

# Without KV cache — recomputes all previous tokens each step
start = time.time()
output_no_cache = model.generate(
    **inputs,
    max_new_tokens=30,
    use_cache=False  # forces recomputation
)
print(f"Without KV cache: {time.time()-start:.3f}s")
# Result: Without cache is noticeably slower, especially for longer sequences

🧩 概念总结 / Summary

技术解决的问题代价
KV Cache避免重复计算 attentionGPU 显存
PagedAttentionKV Cache 碎片化工程复杂度
KV QuantizationKV Cache 显存占用微小精度损失
Speculative Decoding串行生成速度慢需要维护小模型

🧒 ELI5

KV Cache 就像做笔记:老师讲了前面的内容,你记下来,不用每次从头再听一遍。Speculative Decoding 就像一个快嘴助手帮你先猜后面的话,大师一次性确认,比一字一字说快多了!

KV Cache is like taking notes in class — you don't re-listen to everything the teacher said before answering. Speculative Decoding is like a fast-talking assistant who guesses ahead; the expert confirms multiple guesses at once instead of speaking word by word.


📚 References

Attention Heatmap

When the model processes it, it attends most strongly to animal.

Theanimaldidntcrossthestreetbecauseitwastootired
it →