Day 58
系统设计:从需求推到取舍
系统设计不是把 Redis、Kafka 和微服务画在一张图上。它要说明:用户需要什么,哪些约束最重要,数据怎样流动,故障出现时系统如何表现,以及为什么选择这套方案。
今日目标
- 用一套可复用的问题框架组织设计过程
- 按已声明的假设估算流量和存储量级
- 说清 CAP 讨论发生的前提,避免把概念当成架构标签
- 完成一个短链接服务设计,并独立练习限流器、实时聊天题
1. 先澄清需求与约束
开始画图前,先问清“做什么”和“做到什么程度”:
功能需求:谁能创建短链接?访问短链接时发生什么?是否需要自定义别名、过期和禁用?
非功能需求:更看重跳转可用性、数据一致性、延迟还是成本?预期用户量、读写比例和保留时间是多少?哪些数据必须在写入后立即可见?
如果面试官没有给数字,可以主动声明一组假设并做量级估算。假设是推理输入,不是事实;改一倍、十倍后结论是否变化,也应说出来。
2. 用短链接服务走一遍设计
API 与数据模型
最小功能可以是创建链接和解析链接:
POST /api/links
{ "url": "https://example.com/article" }
→ { "code": "aB91xQ" }
GET /aB91xQ
→ 302 Location: https://example.com/article
数据表至少要表达短码唯一、目标地址和可选过期时间:
| 字段 | 含义 | 约束 |
|---|---|---|
| code | 短码 | 唯一索引 |
| target_url | 目标地址 | 校验协议和长度 |
| created_at | 创建时间 | 生成时写入 |
| expires_at | 可选过期时间 | 读取时检查 |
生成短码可以使用随机值并对唯一冲突重试,也可以用序列 ID 编码。随机值需要处理冲突;可预测 ID 需要考虑枚举和滥用。最终方案由流量、隐私和运维约束决定。
容量估算:把假设写在算式旁边
举例说明方法,以下数字只是练习输入:
- 假设每天创建 100,000 条链接:平均约
100,000 / 86,400 ≈ 1.2次写入/秒。 - 假设每天解析 1,000,000 次:平均约
1,000,000 / 86,400 ≈ 11.6次读取/秒。 - 若先按峰值是平均值的 5 倍估计,读取峰值约 58 次/秒。实际峰值要用业务分布验证。
- 若一行连同索引暂按 500 字节粗估,每天新增约 50 MB 原始存储,尚未计入副本、备份和数据库页开销。
这组估算只帮助决定下一步要问什么:例如峰谷差有多大、点击是否集中在少数热链接、保留期多长。它不能替代压测或生产观测。
高层组件与请求路径
创建:客户端 → API → 参数与滥用校验 → PostgreSQL
访问:浏览器 → 负载均衡 → API → (可选缓存) → PostgreSQL → 302
先用单体 API 和 PostgreSQL 满足基本需求。访问量和延迟数据证明需要缓存后,再考虑缓存热点短码;点击统计可以异步记录,但会带来短暂延迟、重复投递和丢失处理问题。
为创建链接设置幂等键可以避免客户端重试产生多条记录。解析路径应有超时和明确的过期行为;如果数据库暂时不可用,系统不能凭过期缓存继续跳转并声称数据仍然强一致。
3. CAP、BASE 与一致性
CAP 讨论的是发生网络分区时,分布式数据系统对一致性和可用性的取舍:分区期间不能同时保证强一致读写和每个请求都成功响应。网络分区不是可选的设计开关;系统也不是简单地永久选一个字母。
BASE 常用来描述更偏向可用性、允许部分结果暂时不一致并最终收敛的设计倾向。它不是“不要事务”的规则,也没有免除定义一致性边界的责任。
对短链接来说,可以分别讨论:
- 创建成功返回前,短码是否必须能从任意读取节点立即解析?
- 点击统计是否允许延迟几秒汇总?
- 刚禁用的链接是否允许缓存继续跳转?
这三个数据路径可以有不同要求。把整套系统叫作“AP”或“CP”通常不足以回答这些具体问题。
4. 如何组织面试中的回答
按顺序推进,每一步都给面试官留出纠正假设的机会:
- 澄清范围:角色、核心操作、明确不做的功能。
- 声明目标:延迟、可用性、一致性、成本中优先级是什么。
- 估算规模:读写 QPS、峰值系数、数据保留量;写出假设和单位。
- 定义接口和数据:API、主键、唯一性、索引与数据生命周期。
- 画出主路径:先画最少组件,标出同步与异步边界。
- 找瓶颈和故障:热键、数据库不可用、超时、重复请求、缓存过期。
- 讨论演进:说明触发扩容或拆分的证据,以及新方案的成本。
不要在需求还不清楚时先承诺“支持百万并发”。先把规模和质量目标变成可检查的数字或边界。
实践练习:写三份设计笔记
为短链接完成一份完整设计,并分别用同样的框架简要设计一个限流器和实时聊天系统。每份至少回答:
- 主要用例与明确不做的范围;
- 规模假设与简单算式;
- API 和关键数据结构;
- 正常请求路径与一个失败路径;
- 一项关键取舍,以及什么证据会让你改变它。
限流器可以比较固定窗口、滑动窗口和令牌桶;实时聊天可以讨论消息持久化、在线状态、断线重连与重复消息。不要试图一次把所有优化都画进去。
验收清单
- 设计从需求和约束开始,图中每个组件都能对应一个需求
- 容量估算写明假设、单位和峰值系数
- 能解释 CAP 的分区前提,以及读写路径的一致性要求
- 三个设计题各有正常路径、失败场景和取舍
- 能指出哪些数字只是估算,哪些需要实际压测或监控验证
参考资料
本文内容来自 源仓库 day-58/README.md