系统设计框架
- 弄清具体要求
- 功能需求
- 考虑最重要的三个功能
- 非功能需求
- 一些基本的取舍(trade-offs)
- 功能需求
- 核心 entities
- 系统中的参与者有哪些?满足功能需求需要什么资源?
- 高层架构
- 用方框和箭头表示系统的不同组件及其交互方式
- 组件:基本技术模块,例如服务器、数据库、缓存等
- 数据如何在系统中流动,以及每次请求(从 API 请求到响应)都会改变哪些状态
- API /接口
- 使用哪种协议(外部 API 用 REST,内部调用用 gRPC)
- 深挖,一些独立的问题
- 非功能需求
- 极端情况和瓶颈
非功能需求
- 一致性
- 强一致性 (Strong Consistency):任何时候读到的都是最新写入的数据
- 最终一致性 (Eventual Consistency): 允许短时间内数据不一致,但最终会同步
- 分布式事务:一个操作跨越多个数据库/服务时,如何处理?
- 可用性
- 系统能否一直在线,挂了怎么办?
- 实时性
- 响应有多快,延迟是多少?
- 扩展性
- 用户从 1 万增长到 1 亿时,系统能不能通过增加机器来扛住压力?
- 垂直扩展 (Vertical Scaling):给原来的电脑换更强的 CPU、更大的内存(有上限)
- 水平扩展 (Horizontal Scaling):多几台普通的电脑一起工作(理论上没有上限,这是系统设计的核心)
- 持久
- 存进去的数据不会丢:多副本
- 可靠性
- 系统是否不出错
- 幂等性
- 在分布式环境下,由于重试机制(Retry),同一个请求可能会发送多次。如何保证“支付一次”的请求即便发了三次,用户也只被扣一次钱?
- 吞吐
- QPS/TPS,并发数
Trade-offs: CAP
可用性(Availability)和一致性(Consistency)不能同时满足
前提:分区容错性(Partition Tolerance,即网络断开后系统还能继续运行)是必选项
为什么?
假设一个简单系统,只有两台服务器 Node A 和 Node B,它们各自存有一份同样的数据库副本。现在:
- 网络断开了,Node A 和 Node B 之间无法通信(发生了 Partition)。
- 写操作:用户向 Node A 发送请求,把自己的余额从 100 改成了 50。
- 由于网络断开,Node A 无法告诉 Node B 余额变了。
- 读操作:此时另一个用户访问 Node B,问余额是多少。
现在,系统只有两个选择:
选择一:保证一致性 (CP)
如果想保证一致性 (C),那么 Node B 不能返回那个旧的 100。
- Node B 发现自己没法和 Node A 同步,于是它决定直接报错,或者告诉用户“系统维护中”。
- 结果:数据是对的(没有返回错误数据),但系统不可用 (A) 了。
选择二:保证可用性 (AP)
如果想保证可用性 (A),那么无论如何都要给用户一个响应。
- Node B 直接返回了它手里现有的数据 100。
- 结果:系统一直在运行(可用),但数据是错的(Node A 是 50,Node B 是 100),不一致 (C) 了。
除此之外,比较常见的 trade-offs 还有:
- 延迟和吞吐
- 延迟和一致性
- SQL 和 NoSQL
- ACID 和 BASE
- 有状态和无状态
- 读优化和写优化
- Push 和 Pull
- ……
要根据具体的场景来决定讨论哪些 trade-offs。
核心概念
- 计算机网络
- HTTP
- WebSocket
- 负载均衡
- CDN
- API 设计
- REST
- gRPC
- 数据建模
- 关系型数据库
- NoSQL
- 数据库索引
- B 树
- 缓存
- Redis
- 缓存失效
- 缓存崩溃
- 分片
- shard key
- 一致性哈希
- 需要知道的一些数字,用于估算性能
常见技术
系统设计就是把技术模块组合起来解决问题,因此需要知道有哪些常用的技术。对每种技术(e.g., 消息队列)应该都至少掌握一种具体的方案(e.g., Kafka)。
- 负载均衡:Nginx
- 把流量平均分配给多台服务器
- API 网关:Spring Cloud Gateway
- 鉴定权限、限流、路由
- CDN:Cloudflare
- 把静态资源缓存在距离用户较近的服务器上
- 消息队列:Kafka, RabbitMQ
- 异步处理,应对突发流量、分配系统负载
- 分布式缓存:Redis
- 把高频访问的数据存入内存,降低读取延迟
- 数据库
- 关系型数据库:MySQL, Postgres
- NoSQL 数据库:DynamoDB, MongoDB
- Blob 对象存储:AWS S3
- 搜索优化数据库:Elasticsearch
架构演进的逻辑:
- 单机,all-in-one
- 拆分数据库与应用服务器
- 引入缓存,减轻 DB 压力
- 引入负载均衡与水平扩展,解决高并发
- 数据库读写分离/分库分表,解决数据量瓶颈
- 引入消息队列(异步、削峰)
- 微服务化,复杂业务解耦
模式
- 写多读少,异步处理
- 打车(轨迹上报)
- Notification
- 读多写少,低延迟、弱一致性
- Feed 流
- 电商(浏览)
- 调度系统,强一致性
- 支付
- 秒杀/票务
- 打车(实时匹配)
- 实时交互,长连接状态保持
- 即时通讯
- 多人协作文档
- 复杂数据模型与查询
- 搜索引擎
- 推荐系统
- 有状态的中间件,infra 设计
- 分布式缓存
- 消息队列