🔄 复习日 / Review Day — Day 75 (2026-06-24)
今天是复习日!回顾过去几天的内容。
It's review day! Let's revisit the past few days.
📝 Quick Quiz — 3 Mini-Reviews
从3个不同模块各取1道题 / One question from 3 different sections
Q1: [🏗️ System Design] — Day 74: Design an AI Chatbot Platform (ChatGPT-scale)
你正在为一个 ChatGPT 级别的 AI 对话平台设计请求路由层。用户请求是流式的(streaming),全球有数百万并发用户。你如何设计负载均衡层来保证同一会话的连续性,同时支持水平扩展?
You're designing the request routing layer for a ChatGPT-scale AI chat platform. Requests are streaming (SSE/WebSocket) with millions of concurrent users globally. How do you design the load balancer layer to ensure session continuity while supporting horizontal scaling?
<details><summary>显示答案 / Show Answer</summary>
核心挑战: 流式响应(SSE/WebSocket)需要长连接,传统的无状态轮询负载均衡会打断会话。
解决方案 — 两层路由:
- 会话亲和性(Sticky Sessions)+ 一致性哈希:基于
session_id或user_id哈希,将同一用户的请求始终路由到同一 API Gateway 节点。用一致性哈希(Consistent Hashing)确保节点增减时影响最少的会话。 - 无状态 Inference Worker:会话状态(KV Cache)不存在 worker 本地,而是写入分布式缓存(Redis Cluster)。Gateway 读取 KV Cache 后转发给任意可用 worker — 解耦路由层与计算层。
- 故障转移:若某个 Gateway 节点宕机,一致性哈希环自动将该节点的 session 重新映射到相邻节点,从 Redis 恢复 KV Cache 继续推理。
Key insight: Decouple session routing (sticky at gateway) from compute (stateless workers). Store KV Cache in Redis so any worker can resume any session — enabling true horizontal scale without session loss.
</details>
Q2: [💻 Algorithms] — Day 73: Serialize and Deserialize Binary Tree
给出以下二叉树:
1
/ \
2 3
/ \
4 5
用 BFS(层序)序列化后的字符串是什么?反序列化时,如何区分一个节点"没有左子节点"和"没有右子节点"?
Given the binary tree above, what does the BFS-serialized string look like? When deserializing, how do you distinguish "no left child" from "no right child"?
<details><summary>显示答案 / Show Answer</summary>
序列化结果: "1,2,3,null,null,4,5"
BFS序列化过程:
# 用 queue,层序遍历,null节点也入队列(但不展开)
# Level 0: [1]
# Level 1: [2, 3]
# Level 2: [null, null, 4, 5] ← 2的两个null子节点 + 3的两个子节点
如何区分左右子节点缺失:
关键在于位置编码。BFS序列化时,每个节点(包括 null)按严格的左-右顺序入队。反序列化时,用一个指针 i 从 i=1 开始,每次弹出一个父节点,就消耗 tokens[i](左子)和 tokens[i+1](右子)。null 占位符保证了位置信息不丢失。
Key insight: It's all about positional encoding. null placeholders act as tombstones that preserve the left/right relationship. Without them, 1,2,3,4,5 is ambiguous — you can't tell if 4 is a left or right child of 3.
</details>
Q3: [⚙️ Python Craft] — Day 73: Garbage Collection — Reference Counting + Generational GC
以下 Python 代码会导致内存泄漏吗?为什么?Python 的哪个机制负责处理这种情况?
class Node:
def __init__(self, val):
self.val = val
self.next = None
a = Node(1)
b = Node(2)
a.next = b
b.next = a # 循环引用!
del a
del b
# 此时内存被释放了吗?
Does this Python code cause a memory leak? Why? Which Python mechanism handles this?
<details><summary>显示答案 / Show Answer</summary>
答案:不会永久泄漏,但引用计数单独无法处理。
引用计数分析:
del a后:a对象的 refcount 从 2 降到 1(因为b.next仍然指向它)del b后:b对象的 refcount 从 2 降到 1(因为a.next仍然指向它)- 两个对象的 refcount 都是 1,不为 0,引用计数无法回收它们
谁来救场:分代垃圾回收器(Generational GC)
CPython 的 gc 模块实现了标记-清除(Mark and Sweep)算法,专门处理循环引用。它周期性地扫描第 1 代和第 2 代对象,找到"不可达"的循环引用组并批量回收。
import gc
gc.collect() # 手动触发,通常不需要
Key insight: Reference counting is O(1) and immediate but blind to cycles. The generational GC is the safety net — it runs less frequently (triggered by allocation thresholds) and catches what refcounting misses. For performance-critical code, avoid creating cycles or use weakref.
</details>
💡 复习巩固记忆,螺旋式上升。Spaced repetition beats cramming — every review compounds.
📅 明天继续新内容!See you tomorrow for Day 76.