← 2026-06-23 📂 All Days 2026-06-25 →

🔄 复习日 / 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)需要长连接,传统的无状态轮询负载均衡会打断会话。

解决方案 — 两层路由:

  1. 会话亲和性(Sticky Sessions)+ 一致性哈希:基于 session_id 或 user_id 哈希,将同一用户的请求始终路由到同一 API Gateway 节点。用一致性哈希(Consistent Hashing)确保节点增减时影响最少的会话。
  2. 无状态 Inference Worker:会话状态(KV Cache)不存在 worker 本地,而是写入分布式缓存(Redis Cluster)。Gateway 读取 KV Cache 后转发给任意可用 worker — 解耦路由层与计算层。
  3. 故障转移:若某个 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.