跳到主要内容
预计阅读 23 分钟

10 | 架构演进与面试 —— 架构演进案例、系统设计面试框架、Trade-off 思维

没有完美的架构,只有合适的架构。系统设计的本质不是找到最优解,而是在约束条件下做出最合理的取舍。


开篇自测

  1. 一个日活百万的电商系统,从单体演进到微服务,中间一般经历哪些关键里程碑?
  2. 在系统设计面试中,面试官最看重的是最终方案,还是思考过程?
  3. 什么是 Trade-off 思维?为什么说它是高级工程师的核心能力?

一、架构演进实战:一个电商平台的成长史

1.1 阶段一:创业期的单体应用

2019 年,某团队用 Spring Boot 单体启动生鲜电商——用户、商品、订单、支付全在一个仓库,共用一个 MySQL。

阶段一(DAU < 5万,团队 6 人)

+----------------------------------------------+
|              Spring Boot 单体应用              |
|  UserModule | OrderModule | ProductModule    |
|  PayModule  | DeliveryModule                 |
+----------------------+-----------------------+
                       |
                 +-----v-----+
                 |   MySQL    |     单台 Nginx 部署
                 |  单库单表   |
                 +-----------+

优点:开发快、部署简单、调试方便

这个阶段单体是最优解——创业公司最稀缺的是时间,不是性能。

1.2 阶段二:垂直拆分

2020 年 DAU 突破 20 万,痛点出现:每次发布重启整个应用、慢 SQL 拖垮全局、数据库连接打满。

阶段二(DAU ~ 30万,团队 20 人)

+----------+   +----------+   +-----------+
| 用户服务  |   | 交易服务  |   | 商品服务   |
+----+-----+   +----+-----+   +-----+-----+
     |              |               |
+----v-----+   +----v-----+   +----v------+
| 用户 DB  |   | 订单 DB  |   | 商品 DB   |
+----------+   +----------+   +-----------+

引入: Redis 会话缓存 + Nginx 负载均衡

1.3 阶段三:微服务化

2021 年大促 QPS 峰值 5 万,订单和支付耦合导致支付超时:

阶段三(DAU ~ 100万,团队 50 人)

                  API 网关 (Kong)
                       |
    +------+-------+---+----+-------+-------+
    |      |       |        |       |       |
  用户   商品    订单     支付    配送    通知
  服务   服务    服务     服务    服务    服务
    |      |       |        |       |       |
  各自独立数据库 + Kafka + Nacos + SkyWalking + ELK

1.4 阶段四:平台化中台

2023 年拓展社区团购、企业团餐,大量能力重复建设:

阶段四(DAU ~ 500万,团队 200 人)

  前台业务:  生鲜App | 社区团购小程序 | 企业团餐SaaS
  业务中台:  用户中台 | 交易中台 | 营销中台 | 配送中台
  数据中台:  实时数仓 | 用户画像 | AB 实验
  技术底座:  K8s 容器 | 服务网格 | 可观测性平台

1.5 演进复盘

阶段痛点决策结果
单体无(快速验证)Spring Boot 单体3 月上线 MVP
垂直拆分部署耦合、DB 瓶颈按业务域拆应用和数据库故障隔离
微服务服务内耦合、扩展困难细粒度服务 + MQ弹性扩缩容
中台多业务线重复建设沉淀通用中台能力新业务 2 周上线

二、系统设计面试框架:RESHADED 方法

RESHADED 是本课程总结的面试框架(非业界通用标准),融合了 Alex Xu《System Design Interview》四步法等方法论并做了更细化的拆解,帮助你在面试中保持结构清晰。

2.1 面试的考察维度

系统设计面试不是考你背方案,而是考工程思维

+------------------+--------+------------------------------+
|     评分维度      |  权重  |         考察要点              |
+------------------+--------+------------------------------+
| 需求分析         |  20%   | 主动澄清需求、识别核心功能     |
| 高层设计         |  20%   | API、数据模型、架构图          |
| 深度设计         |  25%   | 关键组件详细设计与算法选择      |
| Trade-off 讨论   |  25%   | 方案对比、权衡决策            |
| 沟通表达         |  10%   | 逻辑清晰、主动引导            |
+------------------+--------+------------------------------+

2.2 RESHADED 八步法

R - Requirements(需求澄清)      3-5 min
    "核心用户场景是什么?预期 QPS / SLA 是多少?"

E - Estimation(估算)            2-3 min
    "1亿条/天 ≈ 日均1200 QPS ≈ 峰值5000 QPS"

S - Storage schema(存储方案)     3-5 min
    数据库选型、表结构,SQL vs NoSQL 理由

H - High-level design(高层设计)  5-8 min
    核心组件图和数据流

A - API design(接口设计)        3-5 min
    核心 API 签名和语义

D - Deep dive(深度设计)         8-10 min
    对 1-2 个关键组件做详细设计

E - Edge cases(边界情况)        3-5 min
    异常处理、故障恢复、一致性

D - Discussion(扩展讨论)        3-5 min
    水平扩展、监控、未来演进

2.3 面试示例:设计短链服务

# R: 长链->短链,短链->重定向。读多写少,<50ms延迟,永不过期
# E: 读 QPS ~5000(峰值), 写 ~50, 年存储 ~73GB
#    注:此处假设日活较小(约50万)因此存储量远小于第1章社交平台百亿级场景,
#    面试中估算结果取决于需求假设,关键是推导过程合理。
# S: MySQL(数据量不大) + Redis(热链缓存)

# A - API Design:
# POST /api/v1/links {"url":"https://..."} -> {"short_url":"s.co/aB3xK9"}
# GET /{short_code} -> 301 Redirect

# D - Deep Dive: 短码生成
import hashlib, string

CHARSET = string.ascii_letters + string.digits  # 62字符
CODE_LEN = 7  # 62^7 ≈ 3.5万亿

def base62_encode(num):
    if num == 0: return CHARSET[0]
    result = []
    while num > 0:
        result.append(CHARSET[num % 62])
        num //= 62
    return "".join(reversed(result))

def gen_short_code(url, attempt=0):
    """哈希 + 冲突重试"""
    raw = f"{url}:{attempt}"
    h = int(hashlib.md5(raw.encode()).hexdigest()[:11], 16)
    return base62_encode(h)[:CODE_LEN]

print(gen_short_code("https://example.com/very/long/path"))

# E - Edge: 哈希冲突 -> attempt+1 重试; Redis宕机 -> 降级查DB
# D - Discussion: 301 vs 302 取决于是否需要统计点击量

三、Trade-off 思维:没有银弹,只有权衡

3.1 什么是 Trade-off

Trade-off 不是”A 比 B 好”,而是清楚 A 在什么条件下比 B 好,以及为此付出了什么代价

Trade-off 三角

         性能
        /    \
       /  三选二 \
      /          \
   成本 -------- 可靠性

高性能+高可靠 = 高成本(多副本+高端硬件)
高性能+低成本 = 低可靠(单机无备份)
高可靠+低成本 = 低性能(廉价硬件+纠删码)

3.2 常见 Trade-off 速查

Trade-off选 A选 B判断依据
一致性 vs 可用性CP(强一致)AP(高可用)金融选 CP,社交选 AP
延迟 vs 吞吐低延迟(同步)高吞吐(异步批量)实时交互 vs 数据管道
读优化 vs 写优化冗余缓存范式化追加写读多写少 vs 写多读少
灵活 vs 简单微服务单体大团队复杂业务 vs 小团队
空间 vs 时间缓存换速度压缩换空间看哪个资源更稀缺

3.3 面试中如何展示

初级: "我选 Redis 做缓存"           —— 只有结论
中级: "选 Redis,因为它快"          —— 理由太笼统
高级:
  "选 Redis 而非 Memcached,因为:
   1. 需要丰富数据结构(排行榜用 ZSet)
   2. 需要持久化(AOF 防缓存雪崩冷启动)
   3. 需要 Lua 原子操作(库存扣减)
   Trade-off: Redis 单线程在大 value 下会阻塞,
   我们控制 value < 10KB 来规避;内存成本高于
   Memcached,但数据量约 50GB,成本可控"

四、容量估算:面试中的数学题

4.1 核心公式

DAU × 人均操作次数 = 日请求总量
日请求 / 86400(≈10^5) = 平均 QPS
平均 QPS × 峰值系数(2~10) = 峰值 QPS
单条大小 × 日新增 × 365 × 年限 = 年存储量

心算近似: 1天≈10^5秒, 1年≈3×10^7秒
         1KB=10^3, 1GB=10^9, 1TB=10^12

4.2 估算实战:聊天系统

条件: DAU=5000万, 人均20条/天, 每条200B, 存5年, 峰值系数3

消息量: 5×10^7 × 20 = 10^9 条/天
写QPS:  10^9 / 10^5 = 10000,  峰值 = 30000
读QPS:  写的 5~10 倍 ≈ 150K~300K

日存储:  10^9 × 200B = 200GB/天
年存储:  200GB × 365 ≈ 73TB/年
5年存储: 365TB, 含3副本 ≈ 1PB

结论: 分布式存储(HBase/Cassandra) + Redis 缓存近期消息
      WebSocket 长连接 + MQ 推送 + CDN 多机房

五、面试高频题速查

5.1 题目分类

                数据密集型(I/O)        计算密集型(CPU)
             +------------------+------------------+
  存储类      | Feed流 / 聊天     | 搜索引擎 / 推荐    |
             | 网盘 / 文件存储   | 爬虫系统           |
             +------------------+------------------+
  服务类      | 短链 / 评论       | 秒杀 / 抢红包      |
             | 通知系统          | 实时竞价(RTB)      |
             +------------------+------------------+

5.2 Feed 流:Push vs Pull

Push(写扩散): 发帖 -> 写入所有粉丝收件箱
  写放大, 读简单。适合粉丝少的普通用户。

Pull(读扩散): 发帖 -> 只写自己发件箱, 读时拉取合并
  写简单, 读放大。适合粉丝多的大V。

工业方案: 混合模型
  粉丝<1000 -> Push
  粉丝>10万 -> Pull
  中间      -> 活跃粉丝Push + 不活跃Pull

5.3 限流算法对比

算法优点缺点场景
固定窗口实现简单窗口边界突发粗粒度限流
滑动窗口精确控制内存开销大精确限流
漏桶流量平滑无法突发匀速消费
令牌桶允许突发实现稍复杂API 限流

六、面试常见陷阱与节奏控制

6.1 五大陷阱

1. 上来就画架构图
   -> 先确认需求,再画图

2. 过度设计
   -> 当前量级够用即可,说明未来扩展路径

3. 只说方案不说原因
   -> "因为数据灵活+主键查询为主+不需复杂事务,所以选 MongoDB"

4. 忽略非功能需求
   -> 主动提出 SLA、延迟 P99、可用性目标

5. 不做估算直接选型
   -> "预估3年500GB、5亿行,超出单表最佳实践,需分库分表"

6.2 45 分钟节奏

[0-5 min]   需求 + 估算
[5-15 min]  高层设计(核心组件图 + 数据流)
[15-35 min] 深度设计(1-2 个关键组件 + Trade-off)
[35-45 min] 扩展讨论(水平扩展 + 监控 + 演进)

主动控制节奏:
  "需求已明确,我先画高层架构"
  "高层完成,我想深入 X 组件的设计"
  "如果时间允许,聊一下扩展方案"

七、知识图谱与面试清单

系统设计知识图谱

  设计方法论: 需求 -> 估算 -> 高层 -> 详细 -> Trade-off
       |            |            |
  +----v----+  +----v----+  +----v----+
  | 高性能   |  | 高可用   |  | 可扩展   |
  | 缓存     |  | 冗余     |  | 分层     |
  | 读写分离 |  | 限流熔断 |  | 微服务   |
  | 分库分表 |  | CAP/BASE |  | 中台     |
  +---------+  +---------+  +---------+
       |            |
  +----v-----------v-----------+
  | 消息队列 | 分布式基础 | 存储  |
  | Kafka    | 一致性哈希 | SQL   |
  | 事件驱动 | 分布式锁   | NoSQL |
  +----------------------------+
       |
  +----v---------------------------+
  | 经典系统设计                     |
  | 短链|Feed流|IM|抢红包           |
  | 秒杀|推荐|搜索|文件存储          |
  +--------------------------------+
checklist = {
    "基础概念": ["CAP vs BASE", "一致性哈希", "分布式事务(2PC/TCC/Saga)",
                "缓存穿透/击穿/雪崩"],
    "存储选型": ["MySQL vs MongoDB vs Cassandra", "Redis vs Memcached",
                "ES 适用场景与局限"],
    "架构模式": ["读写分离与一致性", "CQRS + 事件溯源", "微服务拆分原则"],
    "经典系统": ["短链短码生成", "Feed流 Push/Pull", "秒杀多级缓存",
                "推荐召回-排序"],
    "软技能":   ["容量估算", "Trade-off 展示", "面试节奏控制"],
}
for cat, items in checklist.items():
    print(f"\n{cat}】")
    for i, item in enumerate(items, 1):
        print(f"  {i}. {item}")

思考题

  1. 假设你负责一个日活 10 万的 SaaS 产品,CTO 提议把单体重构为微服务。你会如何分析这个决策?你的建议是什么?

  2. 回顾你最近参与的一个技术方案,其中做了哪些 Trade-off?如果重新来过,你会做出不同的选择吗?


结尾自测

  1. 架构演进中,从单体到微服务的常见驱动力有哪些?

    • :部署耦合(改一个模块重启全部)、数据库瓶颈(共用 DB)、团队协作困难(多人改同一仓库)、扩展受限(无法单独扩容热点模块)。
  2. RESHADED 框架的八个步骤分别是什么?

    • :Requirements、Estimation、Storage schema、High-level design、API design、Deep dive、Edge cases、Discussion。
  3. Trade-off 思维在面试中如何体现?

    • :不只给结论,而要说”在当前场景选 A 非 B,因为 A 在 X 维度更优,虽然 Y 有牺牲,但约束条件 Z 下可接受”。展示你理解每个方案的优劣和适用边界。
  4. DAU=1000 万、人均 50 次请求,峰值 QPS 约多少?

    • :日请求 = 5*10^8,平均 QPS = 5000,峰值系数 3 则约 15000。
  5. Feed 流中大 V 为什么不用 Push 模型?

    • :大 V 粉丝数百万,一条帖子触发千万级写入(写扩散),延迟高且存储压力大。大 V 用 Pull 模型,粉丝读取时再拉发件箱。

课程终章预告:技术之路没有终点。下一章是结束篇——从系统设计到技术领导力,聊聊如何将系统设计思维应用到技术管理与职业发展中。

购买课程解锁全部内容

面试晋升必学:11 章掌握系统设计

¥29.90