系统设计总览与心智
学系统设计最大的障碍不是「方案不会」,是根本不知道该从哪儿开始想。面试官说「设计一个 Twitter」,新手第一反应是画一个 Spring Boot + MySQL + Redis 的图,然后开始填字段——这就错了 90%。系统设计不是「拼组件」,是先搞清楚问题大小,再决定要不要用重武器。这一篇不讲任何具体方案,只讲一件事:面对「设计 X」时,脑子里到底应该先转什么。
一句话先记住:系统设计 = 把模糊需求翻译成"流量数字 + 数据模型 + 失败假设"三件套,再选最便宜的方案撑住。看上去答案是架构图,实际上 80% 的工作发生在画图之前——没估算清楚 QPS 的架构都是空中楼阁,你不知道每秒 100 请求和每秒 100 万请求是两个完全不同的世界。
一、系统设计的本质:用约束反推方案
CRUD 应用没有「设计」一说——表建好、接口写好、上线。系统设计开始的边界点很清晰:当流量、数据、延迟、可用性中任何一个达到单机扛不住的量级,你就被迫离开 CRUD,进入设计。
设计的本质是在约束里找最便宜的可行解。约束有四类:
| 约束 | 关键问题 |
|---|---|
| 流量 | 每秒多少读 / 多少写?峰值是均值的几倍? |
| 数据 | 存什么、存多大、留多久、怎么查? |
| 延迟 | 用户能等多久?p99 多少?跨地域怎么算? |
| 可用性 | 能不能挂?挂多久?挂了影响多少用户? |
真正的工作流是:先问需求 → 推算约束 → 在约束下选方案,而不是反过来——「先用 Redis Cluster 再倒推为什么要用」就是新手最常见的错误。架构师的功夫,80% 在前两步,只有 20% 在画图。
二、从模糊需求到清晰约束:四步法
面试官扔来一句「设计一个微博」,你不应该直接动笔,应该按这四步问回去:
2.1 第一步:功能边界(做什么、不做什么)
「设计微博」太大,先砍枝:
- 用户发推、看 timeline、关注 / 取关——做
- 评论、点赞——做
- 私信、广告、推荐算法——这一轮不做
- 视频、直播、电商——这一轮不做
这一步不是偷懒,是把无限的问题变成有限的问题。面试 45 分钟讨论不完整个微博,不画边界你必输。真实工作里也一样,做减法是架构师最重要的能力。
2.2 第二步:非功能需求(数字)
砍完功能,问数字:
- DAU(日活)是 1 万、100 万还是 1 亿?
- 写多还是读多?读写比 1:1 还是 1:1000?
- 数据要保留多久?
- 全球还是单地区?
- 容忍多少秒不可用?
这些数字直接决定架构等级。1 万 DAU 单机 + 一个 Redis 就够了;1 亿 DAU 必须分库分表 + 多机房 + CDN。
2.3 第三步:容量估算(下一篇专讲)
把数字翻成 QPS、存储、带宽。一个简化心算:
DAU × 人均请求数 / (24 × 3600) = 平均 QPS
平均 QPS × 5 = 峰值 QPS(常用倍数,具体业务调整)例:1 亿 DAU,人均 50 次请求 → 平均 5.8 万 QPS → 峰值 30 万 QPS。有这个数字,后面所有"用什么 DB"都有根据。
2.4 第四步:核心数据模型
不写表结构,只问三件事:
- 核心实体是什么?(微博:User / Tweet / Follow / Timeline)
- 数据访问模式是什么?(主要是按 user_id 查 timeline,不是全表扫描)
- 量级在哪?(Tweet 表一年 100 亿行,Follow 表一年 10 亿行)
数据模型的关键不是「字段叫什么」,是**「访问模式」+「量级」决定了后面要不要分库分表 / 用 KV / 加缓存**。同样一个 Tweet 表,1 万行你怎么写都行,100 亿行你写错一个索引就爆。
三、设计要展示的是「演进」,不是「终极版」
新手最常见的另一个错误是直接画 Kafka + Cassandra + Spark 的全家桶。这个图不解决任何问题,因为你没说清楚「为什么需要这些」。
正确的做法是从最简单开始,逐步暴露瓶颈,再升级:
V1: 单机
user 表、tweet 表、follow 表都在一台 MySQL
扛得住:DAU < 10 万
V2: 加缓存 + 读写分离
热点 timeline 进 Redis,MySQL 一主多从
扛得住:DAU 100 万
V3: 分库分表 + 异步 fanout
Tweet 按 tweet_id 分,User 按 user_id 分
发推后异步推到关注者收件箱(Push 模型)
扛得住:DAU 1000 万
V4: 推拉结合 + 多机房
大 V 用 Pull(实时拉),普通用户用 Push
多机房读写,异地多活
扛得住:DAU 1 亿+每一步升级都要说清楚:V1 是什么瓶颈卡住的,V2 才合理出现。比如「V1 的 timeline 查询要 join 100 万条 follow,单机 MySQL 撑不住,所以 V2 加 Redis」。这种演进式答题,面试官立刻看出你懂工程——而不是背了一个图。
四、四个永远要回答的「关键取舍」
每个设计决策背后都有取舍,讲不出取舍的设计就是抄答案:
4.1 一致性 vs 可用性(CAP)
- 强一致(转账、库存):宁可不可用,不能多扣钱 → 选 CP
- 最终一致(微博点赞、关注数):稍微不一致没事,要可用 → 选 AP
4.2 读优化 vs 写优化
- 读多写少(微博 timeline、新闻):写时多做事(预计算、推 fanout),读时拿现成的
- 写多读少(日志、监控):写时只追加,读时再算
4.3 同步 vs 异步
- 关键路径(下单、扣款):同步,失败立即返回错误
- 可补偿路径(发邮件、通知、统计):异步走 MQ,失败重试
4.4 存储成本 vs 计算成本
- 算一次存起来重复读(预计算 timeline)= 多花存储,省 CPU
- 每次请求实时算 = 省存储,多花 CPU
任何一个方案上面都能套这四对。面试时把取舍讲明白,比方案本身重要——因为方案永远不止一个,但清晰的取舍说明你知道为什么选它。
五、失败假设:「会挂」是设计前提,不是设计后想
CRUD 思维默认「东西不会挂」,系统设计反过来:默认所有东西都会挂,只是早晚。设计时永远要问:
| 组件 | 它挂了会怎样?对策? |
|---|---|
| 单台 Web 服务器 | 流量切到其他 → 多副本 + LB |
| MySQL 主库 | 写不了 → 自动切主、半同步复制 |
| Redis | 缓存击穿打爆 DB → 多级缓存 + 限流 + 熔断 |
| MQ | 消息堆积或丢 → 持久化 + 重试 + 死信队列 |
| 整个机房 | 全挂 → 多机房 + DNS 切换 |
| 网络分区 | 一半节点联不上 → 共识算法或可降级模式 |
没考虑过失败的设计,是危险的设计。真实事故的诱因 90% 都是「我们没想到这个会挂」。
六、系统设计的三种深度,三种岗位
不是所有「系统设计」都一个量级,粗分三层:
L1 「能跑」 单机 / 简单缓存 / 一两台服务器
CRUD 工程师能做,DAU < 10 万
L2 「扛得住」 分库分表 / 多级缓存 / MQ 异步 / 限流
中级后端的核心战力,DAU < 1000 万
L3 「永远在」 多机房 / 异地多活 / 容灾 / 全球分发
架构师 / 高级,DAU 1 亿+,涉及组织协作大多数面试问到 L2,大多数公司也只需要 L2。L3 是大厂少数核心系统的事(微信、淘宝、Twitter、Stripe),你不在那种岗位的话,精通 L2 就足以拿到 P7 / Senior。本系列重点是 L2,L3 在第四层简要带过。
七、画架构图的最少元素
无论多复杂的系统,架构图就这几个框:
┌─────────┐ ┌──────┐ ┌────────┐ ┌──────┐ ┌──────┐
│ Client │ → │ CDN │ → │ LB │ → │ App │ → │ DB │
└─────────┘ └──────┘ └────────┘ └──────┘ └──────┘
↓ ↑
┌──────┐ ┌──────┐
│ MQ │ → │Worker│
└──────┘ └──────┘
↓
┌──────┐
│Cache │
└──────┘一个合格的架构图必须标清楚:
- 每个箭头是同步还是异步
- 每个组件的副本数 / 分片数
- 数据流方向(读路径 / 写路径分开画)
- 关键的 QPS / 延迟 / 容量数字
没有这些标注的架构图,和小学生画的没区别。架构图不是装饰,是「让别人 30 秒看懂你的设计取舍」的工具。
八、本系列的学习地图
01 总览(这篇)
02-05 心智:估算 / 演进 / CAP / 高可用
看完知道"在想什么",不会再被问懵
06-13 抗压设施:LB / 缓存 / 分库 / MQ / 限流 / 一致性哈希
这一段是面试和实战的"硬通货"
14-24 经典案例:短链 → IM → 秒杀 → 12306
把上面的设施串起来设计真实系统
每篇套同一个五段模板,可以模仿
25-30 进阶:多机房 / 容灾 / 灰度 / 设计文档
大厂场景,按职业阶段决定看不看优先级:01-13 强烈推荐每篇都看;14-24 选感兴趣的;25-30 按需。
九、踩坑提醒(总览版,后面每篇细讲)
- 不问需求就开始画图——做减法和定数字才是关键,画图只是产物
- 用名词堆代替推理——上来就 "ZooKeeper + Kafka + Cassandra",讲不出为什么
- 忽略容量估算——所有「需要 Redis 集群」「需要 MQ」的依据都来自数字,没数字纯靠感觉
- 画终极架构而不是演进——好设计是层层递进推出来的,不是从天而降的
- 不讨论失败模式——「这个组件挂了怎么办」必须每个都答得出
- 强一致和最终一致混淆——转账和点赞是两个世界,搞错就出大事
- 同步当异步用 / 反之——关键路径异步会丢数据,非关键路径同步会拖垮
- 不画读写路径分离——读写通常完全不同,一张图画不清楚
- 忽略「这个数据后期怎么扩」——MySQL 100 亿行不分库,后面是地狱
- 以为「系统设计」是一次画对——不是,是持续演进 + 持续重构
下一篇:02-容量估算.md,讲 QPS / 存储 / 带宽的快速心算法,常见数量级的「肌肉记忆」(1 亿 DAU = 多少 QPS?100 亿条记录占多少磁盘?),和为什么算错一个数量级会让整个架构错。