预计阅读 23 分钟
10 | 架构演进与面试 —— 架构演进案例、系统设计面试框架、Trade-off 思维
没有完美的架构,只有合适的架构。系统设计的本质不是找到最优解,而是在约束条件下做出最合理的取舍。
开篇自测
- 一个日活百万的电商系统,从单体演进到微服务,中间一般经历哪些关键里程碑?
- 在系统设计面试中,面试官最看重的是最终方案,还是思考过程?
- 什么是 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}")
思考题
-
假设你负责一个日活 10 万的 SaaS 产品,CTO 提议把单体重构为微服务。你会如何分析这个决策?你的建议是什么?
-
回顾你最近参与的一个技术方案,其中做了哪些 Trade-off?如果重新来过,你会做出不同的选择吗?
结尾自测
-
架构演进中,从单体到微服务的常见驱动力有哪些?
- 答:部署耦合(改一个模块重启全部)、数据库瓶颈(共用 DB)、团队协作困难(多人改同一仓库)、扩展受限(无法单独扩容热点模块)。
-
RESHADED 框架的八个步骤分别是什么?
- 答:Requirements、Estimation、Storage schema、High-level design、API design、Deep dive、Edge cases、Discussion。
-
Trade-off 思维在面试中如何体现?
- 答:不只给结论,而要说”在当前场景选 A 非 B,因为 A 在 X 维度更优,虽然 Y 有牺牲,但约束条件 Z 下可接受”。展示你理解每个方案的优劣和适用边界。
-
DAU=1000 万、人均 50 次请求,峰值 QPS 约多少?
- 答:日请求 = 5*10^8,平均 QPS = 5000,峰值系数 3 则约 15000。
-
Feed 流中大 V 为什么不用 Push 模型?
- 答:大 V 粉丝数百万,一条帖子触发千万级写入(写扩散),延迟高且存储压力大。大 V 用 Pull 模型,粉丝读取时再拉发件箱。
课程终章预告:技术之路没有终点。下一章是结束篇——从系统设计到技术领导力,聊聊如何将系统设计思维应用到技术管理与职业发展中。
购买课程解锁全部内容
面试晋升必学:11 章掌握系统设计
¥29.90