← 2026-05-26 📂 All Days 2026-05-28 →

🏗️ 系统设计 / System Design — Design a Time-Series Database (InfluxDB)

Day 52 · Expert Phase · ~3 min read


场景 / Scenario

想象你在设计 Datadog 或 Prometheus 的存储后端。每秒要写入数百万条指标数据:CPU 使用率、请求延迟、错误率……这些数据都有一个关键特征:时间是主键。你会如何设计?

Imagine building the storage backend for Datadog or Prometheus. You're ingesting millions of metric data points per second: CPU usage, request latency, error rates. These all share one key property: time is the primary key. How would you design this?


架构图 / Architecture

┌─────────────────────────────────────┐ Producers │ Ingestion Layer │ ───────── │ ┌─────────┐ ┌──────────────┐ │ App Metrics ──────►│ │ HTTP │ │ WAL │ │ Server Stats──────►│ │ API │───►│ (Write-Ahead │ │ IoT Sensors ──────►│ │ │ │ Log) │ │ │ └─────────┘ └──────┬───────┘ │ └────────────────────────┼────────────┘ │ batch flush ┌────────────────────────▼────────────┐ │ Storage Engine │ │ │ │ ┌──────────────────────────────┐ │ │ │ In-Memory Write Buffer │ │ │ │ (TSM Tree hot data ~1h) │ │ │ └──────────────┬───────────────┘ │ │ │ compaction │ │ ┌──────────────▼───────────────┐ │ │ │ Columnar Storage (TSM Files) │ │ │ │ time│cpu│mem│req_rate│... │ │ │ │ ────┼───┼───┼────────┼─── │ │ │ │ t1 │80 │60 │ 1200 │ │ │ │ │ t2 │82 │61 │ 1250 │ │ │ │ └──────────────────────────────┘ │ │ │ │ ┌──────────────────────────────┐ │ │ │ Tag Index (inverted index) │ │ │ │ host=web-01 → [t1,t2,t3...] │ │ │ │ region=us-west → [...] │ │ │ └──────────────────────────────┘ │ └────────────────────┬────────────────┘ │ ┌────────────────────▼────────────────┐ │ Retention & Downsampling │ │ raw: 7d → 1m avg: 30d → 1h avg: 1y │ └─────────────────────────────────────┘

核心设计决策 / Key Design Decisions

1. 列式存储 (Columnar Storage)

为什么? 时序查询几乎总是范围查询("过去1小时的CPU"),列式存储让你只读需要的列。压缩率也更高 — 相邻时间点的值变化小,Delta + Gorilla 编码可压缩 90%+。

Why columnar? Time-series queries are almost always range scans ("last hour of CPU"). Columnar lets you read only the columns you need. Compression is also far better — adjacent values change minimally, and Delta + Gorilla encoding achieves 90%+ compression.

2. TSM Tree (Time-Structured Merge Tree)

类似 LSM Tree,但针对时序优化:

  • Write Buffer → 写入内存,定期 flush 到磁盘
  • Compaction → 合并小文件,有序,利于范围扫描
  • Append-only → 时间天然有序,无需像 B-Tree 那样随机写

Similar to LSM Tree but time-optimized. Writes go to memory buffer, flush to disk periodically. Compaction merges small files. Append-only nature aligns with time's natural ordering.

3. 倒排索引 (Inverted Index for Tags)

tag_index["host=web-01"] → [series_1, series_5, series_12] tag_index["region=us-west"] → [series_1, series_3]

允许高效过滤:SELECT cpu WHERE host='web-01' AND region='us-west'

4. 数据保留策略 (Retention + Downsampling)

Raw data (1s resolution) → keep 7 days 1-minute averages → keep 30 days 1-hour averages → keep 1 year Delete oldest → automatic TTL

这是时序数据库的"超能力" — 自动降精度。


常见错误 / Common Mistakes

❌ 用关系型数据库存时序数据

每秒百万写入 → 行锁、B-Tree 写放大、索引膨胀。PostgreSQL 可以用 TimescaleDB 扩展,但裸 Postgres 扛不住。

❌ Tag cardinality 爆炸

把 user_id 或 trace_id 作为 tag → 倒排索引无限膨胀。Tags 应该是低基数的(host、region、env),高基数数据放 fields。

❌ 忽略时钟漂移

分布式系统中,不同节点的时间戳可能乱序到达。需要处理 out-of-order writes(InfluxDB 有 max-allowed-past-time 配置)。


📚 参考资料 / References


🧒 ELI5

时序数据库就像一本每秒自动记日记的笔记本。普通数据库像一本可以随意翻改的书,时序数据库像一卷纸带 — 只往前走,不往回改,查的时候按时间切一段就行。而且它会自动帮你把昨天的记录"压缩摘要",省空间。

A time-series database is like a notebook that auto-journals every second. Regular databases are like books you can edit anywhere. A TSDB is like a paper tape — always moves forward, never edits old entries, and it automatically "summarizes" old data to save space.

💻 算法 / Algorithms — #23 Merge K Sorted Lists (Hard)

Day 52 · Expert Phase · ~4 min read


🧩 链表技巧模式 (10/11) — building on the Linked List template

今天是链表模式块的倒数第二题,也是本块难度最高的题目。前面我们用快慢指针解决了大部分问题,但 Merge K Sorted Lists 需要一个新武器:最小堆(Min-Heap)。这是 Linked List 模式与 Heap 模式的交汇点。

This is the second-to-last problem in our Linked List block and the hardest. We've used fast-slow pointers for most problems, but Merge K Sorted Lists needs a new weapon: Min-Heap. This is where Linked List meets Heap patterns.


题目 / Problem

🔗 LeetCode #23 — Merge K Sorted Lists 🔴 Hard

📹 NeetCode Video

给你 k 个有序链表,合并成一个有序链表并返回。

Given k sorted linked lists, merge them into one sorted linked list.

Input: lists = [[1,4,5],[1,3,4],[2,6]]
Output: [1,1,2,3,4,4,5,6]

真实类比 / Real-World Analogy

想象你是外卖平台的调度员,手上有 K 条送餐队列,每条队列按预计到达时间排好序。你需要给客户一个统一的"全局最早到达"顺序。每次你要从 K 个队列的队头里,挑出时间最早的那个。

Imagine you're a delivery dispatcher with K sorted queues of orders (sorted by ETA). You need to output a single global earliest-first stream. At each step, you pick the earliest head across all K queues.


解法:最小堆 / Min-Heap Approach

思路 / Intuition

每次我们需要找"K 个链表头节点中最小的那个"。暴力法每次 O(K),K 个节点总共 O(N*K)。用最小堆可以把每次选最小降到 O(log K),总时间 O(N log K)。

import heapq
from typing import Optional, List

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

def mergeKLists(lists: List[Optional[ListNode]]) -> Optional[ListNode]:
    # Min-heap: (value, index, node)
    # index is tie-breaker (ListNode not comparable)
    heap = []
    
    # Initialize heap with head of each list
    for i, node in enumerate(lists):
        if node:
            heapq.heappush(heap, (node.val, i, node))
    
    # Dummy head simplifies edge cases
    dummy = ListNode(0)
    curr = dummy
    
    while heap:
        val, i, node = heapq.heappop(heap)
        curr.next = node        # attach smallest node
        curr = curr.next
        
        if node.next:           # push next from same list
            heapq.heappush(heap, (node.next.val, i, node.next))
    
    return dummy.next

执行追踪 / Trace

lists = [[1,4,5], [1,3,4], [2,6]] Initial heap: [(1,0,L0), (1,1,L1), (2,2,L2)] Step 1: pop (1,0,L0→4) → output 1, push (4,0,L0→4) Step 2: pop (1,1,L1→3) → output 1, push (3,1,L1→3) Step 3: pop (2,2,L2→6) → output 2, push (6,2,L2→6) Step 4: pop (3,1,L1→4) → output 3, push (4,1,L1→4) Step 5: pop (4,0,L0→5) → output 4, push (5,0,L0→5) Step 6: pop (4,1,None) → output 4, no push Step 7: pop (5,0,None) → output 5, no push Step 8: pop (6,2,None) → output 6, no push Result: 1→1→2→3→4→4→5→6 ✅

复杂度 / Complexity

  • Time: O(N log K) where N = total nodes, K = number of lists
  • Space: O(K) for the heap

与模版的关联 / Connection to Template

我们的链表模版是快慢指针 — 今天没有用。但这道题展示了链表问题有时需要其他数据结构辅助:

  • 单链表操作 → 快慢指针
  • 多路归并 → Min-Heap
  • 两两合并 → Divide & Conquer (也可解此题,见下)

备选:Divide & Conquer

def mergeKLists(lists):
    if not lists: return None
    if len(lists) == 1: return lists[0]
    
    mid = len(lists) // 2
    left = mergeKLists(lists[:mid])
    right = mergeKLists(lists[mid:])
    return mergeTwoLists(left, right)  # same as Day 42 #21

同样 O(N log K),且复用了 Day 42 的 mergeTwoLists!


举一反三 / Pattern Connections

本块其他题对比:

题目技巧关键点
#21 Merge Two Sorted Lists双指针K=2 的特殊情况
#23 Merge K Sorted Lists (今天)Min-Heap / D&CK 路归并
#25 Reverse Nodes in K-Group (明天)递归 / 迭代本块压轴

📚 参考资料 / References


🧒 ELI5

有 K 个已经排好序的队伍,你要把他们合成一个大队伍。聪明的办法:用一个"魔法盒子"(最小堆),每次把 K 个队伍的排头兵放进去,自动帮你找出谁最小,让那个人先走,然后再从他的队伍里叫下一个人进来。

You have K sorted lines of people and need to merge them into one line. Smart way: use a magic box (min-heap). Put the front person from each line in the box. The box always tells you who's shortest. That person leaves, and you bring in the next person from their original line.

🗣️ 软技能 / Soft Skills — Architecture Conflict Resolution

Day 52 · Expert Phase · ~2 min read


问题 / Question

"Describe a time you disagreed with a peer on architecture. How did you reach resolution?"

"描述一次你和同事在架构上产生分歧的经历。你是如何解决的?"


为什么面试官问这个 / Why This Matters

架构分歧在工程团队中非常普遍。面试官想知道的不是谁"赢了",而是:

  1. 你能否客观评估技术权衡,而非固执己见
  2. 你是否能在保持关系的同时推进正确的技术决策
  3. Senior/Staff 工程师必须能在没有权威的情况下影响他人

Architecture disagreements are universal. Interviewers want to know: can you objectively evaluate tradeoffs vs. being defensive? Can you advance the right decision while preserving relationships?


STAR 框架 / STAR Breakdown

Situation(情境): 我们的团队在讨论新的通知服务架构。我倾向于用消息队列(Kafka)做异步处理,同事认为应该直接 HTTP 同步调用,更简单。

Task(任务): 在不影响团队关系的前提下,推动一个技术上更合理的决定。

Action(行动):

  1. 先倾听,不急于反驳 — 我先请同事完整解释他的方案,找出合理之处(降低复杂度确实是有效考量)
  2. 数据说话 — 我整理了具体场景:如果通知服务宕机,同步调用会导致上游服务超时;异步队列可以做到故障隔离
  3. 提出折中 — 我们最终采用了消息队列,但同意先用 Amazon SQS 而非 Kafka,降低运维复杂度(部分采纳了他的顾虑)
  4. 写下决策文档 — 记录了两个方案的权衡,未来有新人加入时可以理解背景

Result(结果): 通知服务上线后,主服务的 P99 延迟降低了 40%(因为解耦了慢通知路径),同事也认可了这个方案。


❌ 坏回答 vs ✅ 好回答

❌ "我坚持了自己的方案,因为我是对的,最后他接受了。"

→ 听起来傲慢,无法展示影响力和协作能力

❌ "我们让经理来决定。"

→ 逃避,Senior 工程师不该总是上升到管理层

✅ "我先理解了他的顾虑,发现有部分是合理的,找到了技术上更优但运维上更简单的折中方案。"

→ 展示了技术深度 + 协作能力 + 成熟度


Senior / Staff 加分点 / Advanced Tips

如果对方是更资深的人:

"我会准备书面分析,列出两个方案的具体权衡(成本、风险、可维护性),而不是口头争辩。让数据做裁判。"

如果团队陷入僵局:

提出 proof of concept — "我们花两天分别实现,看看哪个在我们的约束下更合适"

关键原则:

  • 分离技术判断和个人情绪
  • 聚焦系统目标而非个人偏好
  • 记录决策,避免未来再争同一个问题

关键收获 / Key Takeaways

  1. 架构分歧要先倾听再回应,找出对方方案的合理之处
  2. 用具体数据和场景论证,而不是"我觉得"
  3. 寻找技术上更优但兼顾对方顾虑的折中方案
  4. 把决策写下来 — 这本身就是 Senior 工程师的标志

📚 参考资料 / References


🧒 ELI5

两个工程师争架构,就像两个厨师争怎么做汤。聪明的做法不是谁嗓门大谁赢,而是:先尝对方的汤,找到好的部分,然后提议合并两个食谱里最好的地方,最后把这个"最终食谱"写下来,下次就不用再争了。

Two engineers arguing about architecture is like two chefs arguing about a recipe. The smart move: taste each other's soup first, find the good parts, propose combining the best of both, then write down the final recipe so you never have to argue about it again.

🤖 AI — Evaluation: How to Measure AI Quality

Day 52 · Expert Phase · CONCEPT Mode · ~2 min read


核心问题 / The Core Problem

你怎么知道一个 AI 模型"好不好"?准确率 95%?听起来很棒 — 但如果测试集被污染了呢?如果模型"背题"了呢?如果你的任务在测试集上根本没有出现呢?

AI 评估是 AI 开发中最难的问题之一。

How do you know if an AI model is "good"? 95% accuracy sounds great — but what if the test set was contaminated? What if the model memorized answers? What if your real task never appeared in the benchmark?


三层评估框架 / Three Evaluation Layers

Layer 3: Production Monitoring (最终真相) ───────────────────────────────────────── Real user traffic → live feedback → drift detection "Did users actually like the output?" Layer 2: Offline Evaluation (发布前) ───────────────────────────────────── ┌─────────────────┬─────────────────┬─────────────────┐ │ Automatic │ LLM-as-Judge │ Human Eval │ │ Metrics │ │ │ │ (BLEU, ROUGE, │ GPT-4 rates │ Human raters │ │ Exact Match, │ outputs, 80- │ Gold standard │ │ F1, Perplexity)│ 90% agree w/ │ but expensive │ │ │ humans │ │ └─────────────────┴─────────────────┴─────────────────┘ Layer 1: Benchmarks (能力基线) ──────────────────────────────── MMLU, HumanEval, GPQA, SWE-bench... Public, reproducible, but can be gamed

关键评估指标 / Key Metrics

分类任务

from sklearn.metrics import classification_report
# Precision, Recall, F1 per class
# Use when: spam detection, sentiment, intent classification

生成任务 (RAG / QA)

# pip install ragas
from ragas import evaluate
from ragas.metrics import faithfulness, answer_relevancy

# faithfulness: does answer match retrieved context?
# answer_relevancy: does answer address the question?
results = evaluate(dataset, metrics=[faithfulness, answer_relevancy])

LLM-as-Judge 模式

# pip install openai
from openai import OpenAI

def llm_judge(question: str, answer: str, reference: str) -> dict:
    client = OpenAI()
    prompt = f"""
    Rate this answer on a scale of 1-5 for:
    1. Correctness (matches reference)
    2. Completeness (covers all key points)
    3. Clarity (easy to understand)
    
    Question: {question}
    Answer: {answer}
    Reference: {reference}
    
    Return JSON: {{"correctness": N, "completeness": N, "clarity": N, "reasoning": "..."}}
    """
    response = client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": prompt}],
        response_format={"type": "json_object"}
    )
    return response.choices[0].message.content

基准污染问题 / Benchmark Contamination

传统 MMLU 已被大量模型"背熟" — 模型的训练数据包含了测试集。这导致:

MMLU 2024: GPT-4 = 86% ← "amazing!" MMLU-Pro 2025: GPT-4 = 72% ← harder, less contaminated Humanity's Last Exam (2025): Best models ~45% ← expert-level, hard to game

解决方案:

  • 使用动态基准 — 每次评测随机生成题目
  • 使用私有测试集 — 不公开,防止训练数据泄漏
  • SWE-bench — 用真实 GitHub issues 测代码能力,难以"背"

生产环境监控 / Production Monitoring

# Real evaluation: track what actually matters in production
metrics_to_monitor = {
    "user_thumbs_up_rate": "> 80%",      # direct user feedback
    "hallucination_rate": "< 5%",         # factual errors
    "latency_p99": "< 2000ms",            # user experience
    "refusal_rate": "1-5%",               # too high = over-cautious
    "context_relevance": "> 0.85",        # RAG quality
}
# Tools: Langfuse, Arize AI, W&B Weave, Humanloop

📚 参考资料 / References


🧒 ELI5

衡量 AI 好不好,就像评分一个学生的期末考试。问题是:如果这个学生提前看到了考题(数据污染),成绩就不可信了。所以现在大家开始用"每次临时出题"(动态基准),或者直接看这个学生在真实工作中表现如何(生产环境监控)。

Measuring AI quality is like grading a student's final exam. The problem: if the student saw the questions ahead of time (data contamination), their score is meaningless. So now we use "dynamically generated questions" (dynamic benchmarks) or just watch how the student performs in their real job (production monitoring).