← 2026-06-14 📂 All Days 2026-06-16 →

🏗️ 系统设计 / System Design — Day 66

设计搜索自动补全 / Design a Web Search Autocomplete (Google Suggest)

⏱️ 阅读时间:约3分钟 / Reading time: ~3 min


🌏 想象你在设计...

你在负责 Google 搜索栏的自动补全功能。用户每敲一个字符,系统就要在 100ms 内 返回前10条建议。每天有数十亿次请求。这个系统怎么设计?

You're building the autocomplete box for Google Search. Every keystroke must return top-10 suggestions in <100ms. Billions of requests per day. How do you design it?


🏛️ 架构图 / Architecture

User Types "sea" │ ▼ ┌──────────────────┐ │ Browser Cache │◄── short-term prefix cache └──────┬───────────┘ │ cache miss ▼ ┌──────────────────┐ ┌─────────────────────┐ │ API Gateway │────►│ Autocomplete │ │ (rate limit, │ │ Service (stateless) │ │ auth, routing) │ └──────┬──────────────┘ └──────────────────┘ │ │ lookup ┌───────────▼───────────┐ │ Trie Cache Layer │ │ (in-memory Redis) │ │ prefix → top10 terms │ └───────────┬───────────┘ │ cache miss ┌───────────▼───────────┐ │ Trie DB Cluster │ │ (sharded by prefix) │ └───────────┬───────────┘ │ offline build ┌───────────▼───────────┐ │ Data Pipeline │ │ (Kafka → Spark) │ │ search logs → freq │ └───────────────────────┘

⚙️ 核心设计决策 / Key Design Decisions

#### 1. 数据结构:Trie vs. Prefix Hash

Trie(前缀树) 是直觉上的选择,但纯内存 Trie 存10亿词条代价高。

实际方案:

  • 预计算:离线每小时跑 Spark Job,统计 top-K suggestions per prefix
  • 存储:Redis Hash — prefix -> [term1:score1, term2:score2, ...]
  • 键空间:只存长度 ≤ 5 的前缀(覆盖绝大多数真实输入)

In practice: Precompute top-K suggestions offline (Spark/MapReduce), store prefix → top10 in Redis. Don't build a live trie in production.

#### 2. 分片策略 / Sharding

按前缀的第一个字母分片(26个分区),确保每个 shard 负载均衡。

Shard by first character(s) of prefix — ensures balanced load.

#### 3. 更新频率 / Update Frequency

  • 实时更新:新闻热词("earthquake 2026")需要在30分钟内出现
  • 批量更新:常规词条每天重建一次

Two-tier update: streaming pipeline for trending terms (< 30 min lag), batch rebuild for stable terms (daily).

#### 4. 个性化 / Personalization

在通用结果之上,叠加用户历史(浏览器本地存储)。不影响主路径延迟。

Layer personal history on top of global suggestions. Keep personal data in the browser to avoid latency.


🔢 容量估算 / Capacity Estimation

QPS: 10B queries/day ÷ 86400s ≈ 115K QPS × avg 5 keystrokes/query = 575K autocomplete QPS Storage: - 5M unique prefixes (len ≤ 5) - 10 suggestions × 20 bytes = 200 bytes/prefix - Total: ~1 GB → fits in Redis Latency budget: - API Gateway: 5ms - Redis lookup: 1-2ms - Network RTT: 20-50ms - Total: < 100ms ✅

⚠️ 别踩这个坑 / Common Mistakes

❌ 错误做法✅ 正确做法
实时更新 Trie离线批量预计算 top-K
在服务器存个性化数据个性化数据存浏览器本地
所有前缀等权处理只存 ≤5 字符前缀
每次都去 DB 查Redis 缓存 + 浏览器缓存双层
存储完整 Trie存 prefix→top10 映射

🧒 ELI5

像图书馆索引卡片一样。我们提前把"以 'sea' 开头最常搜索的10个词"写在一张卡片上。你一输入 "sea",图书管理员(Redis)直接拿出那张卡片,不用临时翻书。

Like index cards in a library. We pre-write the "top 10 things people search starting with 'sea'" on a card. When you type "sea", the librarian (Redis) just pulls out that card instantly.


📚 References

  • 🔗 https://bytebytego.com/courses/system-design-interview/design-a-search-autocomplete-system
  • 🔗 https://engineering.linkedin.com/blog/2016/03/instant-typeahead-search
  • 🔗 https://www.youtube.com/watch?v=us0qySiUsGU (NeetCode System Design)

💻 算法 / Algorithms — Day 66

#1448 Count Good Nodes in Binary Tree (Medium)

⏱️ 阅读时间:约4分钟 / Reading time: ~4 min

🔗 LeetCode 🟡 Medium | 📹 NeetCode


🧩 树遍历模式 (10/15) — 在 Day 57 的模版基础上延伸

🧩 Trees (10/15) — building on the DFS template from Day 57

今天的问题是带"路径状态"的 DFS 变体:在递归过程中,我们需要把当前路径最大值传递下去。

This is a DFS with carried state variant: pass the max value seen on the current root-to-node path down through recursion.


🌏 真实世界类比 / Real-World Analogy

想象你在爬山,从山脚到每个地点有一条路径。一个地点是"好地点",如果你到达那里的时候,它的海拔不低于路上经过的任何一个地点。问:共有多少个"好地点"?

Imagine hiking: a location is "good" if its elevation is ≥ all previous stops on that trail from base. Count such locations.


📖 问题描述 / Problem

给一棵二叉树,若从根到节点 X 的路径上,没有任何节点的值 大于 X.val,则 X 是好节点。返回好节点的总数。

A node X is good if no node on the path from root to X has a value greater than X.val. Return the count of good nodes.

3 / \ 1 4 / / \ 3 1 5 Good nodes: 3 (root), 3 (left-left), 4, 5 → Answer: 4

🗺️ 映射到模版 / Map to Template

def dfs(node):
    if not node: return BASE_CASE      # 0
    left = dfs(node.left)
    right = dfs(node.right)
    return COMBINE(node.val, left, right)

今天的变化:模板加一个参数 max_so_far

def dfs(node, max_so_far):
    if not node: return 0              # BASE_CASE = 0
    
    is_good = 1 if node.val >= max_so_far else 0
    new_max = max(max_so_far, node.val)
    
    left = dfs(node.left, new_max)     # pass state DOWN
    right = dfs(node.right, new_max)
    
    return is_good + left + right      # COMBINE

核心洞察 / Key insight: 路径最大值是"向下传递的状态",不是向上聚合的结果。比较:求直径时最大深度是"向上聚合"的。

Path max is top-down state; contrast with diameter where max depth is bottom-up aggregated.


🐍 Python 解法 with Trace

class Solution:
    def goodNodes(self, root: TreeNode) -> int:
        def dfs(node, max_val):
            if not node:
                return 0
            
            # Is this node good?
            is_good = 1 if node.val >= max_val else 0
            
            # Update max for children
            new_max = max(max_val, node.val)
            
            return is_good + dfs(node.left, new_max) + dfs(node.right, new_max)
        
        return dfs(root, float('-inf'))  # root is always good

# Trace on example:
# dfs(3, -inf): 3 >= -inf ✅, new_max=3
#   dfs(1, 3):  1 >= 3? ❌, new_max=3
#     dfs(3, 3): 3 >= 3? ✅ → 1
#   dfs(4, 3):  4 >= 3? ✅, new_max=4
#     dfs(1, 4): 1 >= 4? ❌ → 0
#     dfs(5, 4): 5 >= 4? ✅ → 1
# Total: 1 + 0 + 1 + 1 + 1 = 4 ✅

复杂度 / Complexity:

  • ⏱️ Time: O(n) — visit each node once
  • 💾 Space: O(h) — recursion stack, h = tree height

🔁 举一反三 / Pattern Connections

ProblemWhat you pass downWhat you return up
Max Depth (#104, Day 57)nothingmax depth
Good Nodes (#1448, Today)max_so_farcount
Path Sum IIremaining targetlist of paths
Validate BST (#98, Day 67)(min, max) boundsbool
**规律 / Pattern:** 需要路径上的累积信息 → 往下传参数。需要子树的聚合结果 → 从下往上返回。
*Need cumulative path info → pass it down. Need subtree aggregation → return it up.*

🧒 ELI5

你走迷宫,随身带一个"见过的最高数字"的记事本。每到一个房间,如果这个房间的数字 ≥ 记事本上的数字,就是"好房间",把记事本更新。最后数有几个好房间。

Walk a maze carrying a notepad showing "highest number seen so far." A room is "good" if its number ≥ notepad. Update notepad, keep walking. Count good rooms.


📝 Quiz

{"question":"Count Good Nodes: what's the initial value passed to root?","options":["0","float('-inf')","root.val","None"],"correct_index":1}

📚 References

  • 🔗 https://leetcode.com/problems/count-good-nodes-in-binary-tree/
  • 🔗 https://neetcode.io/problems/count-good-nodes-in-binary-tree
  • 🔗 https://www.youtube.com/watch?v=7cp5imvDzl4 (NeetCode video)

🗣️ 软技能 / Soft Skills — Day 66

利益相关者管理 / Stakeholder Management

⏱️ 阅读时间:约2分钟 / Reading time: ~2 min

问题 / Question: "Describe a time you raised concerns about a project direction. How did stakeholders react?"


为什么这道题很重要 / Why This Matters

Senior/Staff 工程师不只是执行者,更是项目的质量守护者。能在合适的时机、以合适的方式提出反对意见,是区分 L5/L6 的关键信号。

Senior/Staff engineers aren't just executors — they're quality guardians. Raising concerns effectively at the right moment, in the right way, separates L5 from L6.

面试官想知道:你是否有勇气说出难听的话?你是否能用数据和逻辑说服别人,而不是情绪?

Interviewers want to know: Do you have the courage to speak up? Can you persuade with data, not emotion?


🌟 STAR 框架拆解 / STAR Breakdown

Situation(背景):

"我们团队正准备在 Q3 上线一个新的支付流程改版,PM 和设计已经花了3个月,产品路演结果也很好。"

Task(任务):

"我在做技术 Review 时发现,新流程在低端设备上有明显的性能问题,预计会影响约15%的用户群体(主要是新兴市场用户)。"

Action(行动):

"我没有直接在大群里说'这个方案有问题'——那只会制造对立。我先:
1. 收集数据:用 Lighthouse 跑了5款代表性低端机型,截图 + 数字
2. 内部对齐:先找直属 TL 对齐,然后约 PM 单独聊
3. 提方案,不只提问题:'我建议延迟6周,先做性能优化,或者我们可以对低端设备做降级处理。'
4. 明确风险:'如果现在上,预计用户投诉率会上升20%,这对我们的Q4 retention 目标有影响。'"

Result(结果):

"PM 最初有些不情愿(他们已经对外承诺了日期),但数据说话。最终我们选择了分阶段上线,先上高端设备,低端设备版本延迟4周。实际结果:投诉率仅上升了3%,远低于预期。"

❌ 差的回答 vs ✅ 好的回答

❌ Bad:

"我告诉 PM 这个方案有问题,他们不听,但后来上线出了问题,证明我是对的。"

问题:没有行动力,结果是负面的,听起来像在抱怨。

✅ Good:

"我用数据量化了风险,提前对齐关键决策者,并提供了替代方案——最终找到了各方都能接受的折衷路径。"

关键:把阻力变成合作,用数据而非情绪。


👑 Senior/Staff 加分项 / Level-Up Tips

  1. 说出影响范围:"影响约15%的新兴市场用户" — 量化 > 模糊
  2. 展示政治智慧:先私下对齐,再正式提出 — 不打无准备之战
  3. 提方案不提问题:永远带着解决方案来,而不只是抱怨
  4. 接受不完美结果:你的建议被部分采纳也是成功,不是失败

For Staff+: Talk about how you built frameworks/processes so concerns get raised earlier next time — e.g., a "pre-mortem" practice before major launches.


🔑 关键句子模板 / Key Phrases

  • "我在评审时发现了一个潜在风险,量化来看是...所以我建议..."
  • "I surfaced this as a data-backed risk, not a gut feeling, and proposed two alternatives..."
  • "我们的讨论帮助团队在 [具体结果] 上达成一致。"

🧒 ELI5

就像在大家都兴奋地准备出发时,你发现车轮快没气了。好的做法不是大喊"不能走!",而是悄悄告诉司机,说"我们先充气,15分钟后更安全地出发"。

Like noticing the tire is low before a road trip. Don't shout "we can't go!" — quietly tell the driver, "let's add air, 15 min saves us a breakdown."


📚 References

  • 🔗 https://www.theengineeringmanager.com/management-101/stakeholder-management/
  • 🔗 https://lethain.com/managing-technical-quality/ (Will Larson on raising quality concerns)
  • 🔗 https://staffeng.com/guides/work-on-what-matters (How Staff engineers prioritize and push back)

🤖 AI — Day 66

AI in Production — Monitoring, Guardrails & Costs

⏱️ 阅读时间:约2分钟 / Reading time: ~2 min


🧠 核心概念 / Core Concept

把 AI 模型部署到生产环境只是第一步。真正的挑战是:它会不会出轨?用多少钱?出问题怎么查?

Deploying an AI model is step one. The real challenges are: Will it misbehave? How much will it cost? How do you debug it when it goes wrong?


🛡️ Guardrails — 运行时护栏

Guardrails 是在 LLM 输入/输出之间插入的运行时校验层,防止:

  • 提示注入攻击(Prompt Injection)
  • PII(个人身份信息)泄露
  • 有害/违规内容输出
  • 幻觉(Hallucination)——输出看起来合理但错误的内容

Guardrails are runtime controls inserted between your app and the LLM. They intercept inputs and outputs to block policy violations.

主流工具 / Key tools (据各平台文档/报道):

  • AWS Bedrock Guardrails — 云原生,集成简单
  • NVIDIA NeMo Guardrails — 开源,可自托管
  • Galileo — 评估 + 可观测性一体
  • Bifrost — 开源 AI Gateway,统一 guardrail 层

Sources: getmaxim.ai, galileo.ai


📊 Monitoring — 可观测性

生产 LLM 的监控比传统服务更复杂:你需要追踪语义质量,不只是延迟/错误率。

Production LLM monitoring is harder than normal services: you need to track semantic quality, not just latency and error rate.

你需要追踪什么 / What to track:

指标工具
Token 用量 & 成本归因Braintrust, LangSmith
延迟(TTFT / total)OpenTelemetry
用户反馈 + 评估分数Opik, Galileo
幻觉率 & 相关性Ragas (for RAG)
安全事件(guardrail 触发)各 Guardrail 平台日志

💰 成本优化 / Cost Optimization

Token 费用是 LLM 最主要的成本,以下是实战策略 (数据据行业报告,结果因场景而异):

1. 模型路由 / Model Routing

简单查询用小模型(GPT-4o-mini、Claude Haiku),复杂任务才用前沿模型。

Route simple queries to cheap models; reserve frontier models for complex tasks. Reportedly reduces average cost-per-query by 37-46%.

2. 语义缓存 / Semantic Cache

相似问题复用缓存答案。Redis 官方 LLM 文档中提到此策略。

Cache responses for semantically similar requests. Can reduce costs up to 73% for repetitive workloads.

3. Prompt 缓存 / Prompt Caching

固定的系统 Prompt 前缀只付一次钱。Anthropic、OpenAI 均已支持。

Reuse stable prompt prefixes — only pay to process them once. Anthropic and OpenAI both support this natively.

4. 上下文压缩 / Context Compression

用小模型先压缩对话历史,再发给大模型。减少 44-89% token(据 codezilla.io 报道)。

Source: aisuperior.com, maviklabs.com


🐍 实战代码 / Runnable Python

# pip install openai tiktoken

import tiktoken
import openai

def count_tokens(text: str, model: str = "gpt-4o") -> int:
    """Count tokens before sending — avoid surprise bills."""
    enc = tiktoken.encoding_for_model(model)
    return len(enc.encode(text))

def smart_route(prompt: str) -> str:
    """Route to cheap model if prompt is simple."""
    token_count = count_tokens(prompt)
    
    if token_count < 500:
        model = "gpt-4o-mini"       # cheap: ~$0.15/1M input tokens
    else:
        model = "gpt-4o"            # powerful: ~$2.50/1M input tokens
    
    print(f"Tokens: {token_count} → Using: {model}")
    
    client = openai.OpenAI()
    response = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}]
    )
    return response.choices[0].message.content

# Example
result = smart_route("What is 2+2?")   # → gpt-4o-mini

🏗️ 生产架构图 / Production Architecture

User Request │ ▼ ┌────────────┐ ┌──────────────────┐ │ Guardrail │───►│ Block/Flag PII, │ │ Gateway │ │ Injection, Harm │ └─────┬──────┘ └──────────────────┘ │ ▼ ┌────────────┐ ┌──────────────┐ │ Semantic │───►│ Cache Hit? │──YES──► Return Cached │ Cache │ └──────┬───────┘ └────────────┘ │ NO ▼ ┌─────────────┐ │ Router │──cheap──► GPT-4o-mini │ (by token │ │ count) │──complex─► GPT-4o └─────┬───────┘ │ ▼ ┌─────────────┐ │ Monitor │ │ (LangSmith │ │ /Braintrust)│ └─────────────┘

🧒 ELI5

把 AI 放到产品里就像雇了一个聪明但有时会乱说话的员工。Guardrail 是经理在旁边监督;Monitoring 是录像机;成本优化是让简单问题找实习生而不是总监回答。

Putting AI in production is like hiring a smart but sometimes unreliable employee. Guardrails = a manager watching over; monitoring = security cameras; cost optimization = routing simple questions to the intern, not the VP.


📚 References

  • 🔗 https://www.getmaxim.ai/articles/top-5-ai-guardrail-solutions-for-production-llm-applications-in-2026/
  • 🔗 https://aisuperior.com/llm-cost-optimization-strategies-2026/
  • 🔗 https://www.braintrust.dev/articles/best-tools-tracking-llm-costs-2026
  • 🔗 https://redis.io/blog/large-language-model-operations-guide/