跳转至

🧭 系统设计方法论

系统设计方法论流程图

学习时间:4小时 | 难度:⭐⭐ 基础 | 前置知识:基本编程和网络知识


🎯 本章目标

  • 掌握系统设计面试的标准4步法流程
  • 区分功能性需求和非功能性需求
  • 熟练进行容量估算(QPS/DAU/存储/带宽)
  • 深入理解CAP定理和BASE理论
  • 掌握水平扩展与垂直扩展的权衡

📋 目录


1. 面试4步法

系统设计面试通常45-60分钟,推荐使用以下4步框架,合理分配时间:

Text Only
┌─────────────────────────────────────────────────────────────┐
│                    系统设计面试4步法                          │
├─────────────┬──────────────┬───────────────┬───────────────┤
│  Step 1     │  Step 2      │  Step 3       │  Step 4       │
│  需求澄清   │  高层设计     │  详细设计      │  扩展优化     │
│  5-10 min   │  10-15 min   │  15-20 min    │  5-10 min     │
├─────────────┼──────────────┼───────────────┼───────────────┤
│ • 功能需求   │ • API设计    │ • 数据模型     │ • 扩展性     │
│ • 非功能需求 │ • 架构图     │ • 核心算法     │ • 监控告警    │
│ • 容量估算   │ • 数据流     │ • 存储方案     │ • 故障处理    │
│ • 边界确认   │ • 技术选型   │ • 缓存策略     │ • 成本优化    │
└─────────────┴──────────────┴───────────────┴───────────────┘

1.1 Step 1:需求澄清(5-10分钟)

核心目标:把模糊的题目转化为明确的设计范围

这是最容易被忽略但最重要的步骤。不要拿到题目就开始画图!

必问的问题清单

Text Only
功能范围:
  - 系统需要支持哪些核心功能?
  - 用户的使用场景是什么?
  - 有哪些功能是不在范围内的?

用户规模:
  - DAU(日活用户)是多少?
  - 读写比例大概是多少?
  - 数据量级是怎样的?

非功能需求:
  - 对延迟的要求是什么(毫秒级?秒级?)
  - 可用性要求是多少(99.9%?99.99%?)
  - 数据一致性要求(强一致?最终一致?)

技术约束:
  - 有技术栈偏好吗?
  - 是否有成本约束?
  - 是建设新系统还是改造旧系统?

实战示例 — 设计Twitter

Text Only
你:这个Twitter系统需要支持哪些核心功能?
面试官:发推、关注/取关、时间线展示

你:用户量级大概是多少?
面试官:DAU 3亿,月活5亿

你:读写比例大概是什么样的?
面试官:读远多于写,大概100:1

你:推文可以包含什么内容?
面试官:文本(140字)和图片

你:时间线有什么特殊要求吗?
面试官:按时间倒序排列,需要实时性

你:可用性方面有什么要求?
面试官:99.99%可用

1.2 Step 2:高层设计(10-15分钟)

核心目标:画出系统的整体架构图

步骤: 1. 定义核心API(RESTful或RPC) 2. 画出主要组件(客户端→LB→服务→存储) 3. 标注数据流向 4. 确认高层设计能满足所有功能需求

API设计模板

Text Only
POST /api/v1/tweet
  - Request: { user_id, content, media_urls }
  - Response: { tweet_id, created_at }

GET /api/v1/timeline?user_id=xxx&page=1&size=20
  - Response: { tweets: [...], next_cursor }

POST /api/v1/follow
  - Request: { follower_id, followee_id }
  - Response: { success }

高层架构图

Text Only
                        ┌──────────┐
                        │  Client  │
                        └────┬─────┘
                        ┌────▼─────┐
                        │   CDN    │ ← 静态资源
                        └────┬─────┘
                        ┌────▼─────┐
                        │    LB    │ ← 负载均衡
                        └────┬─────┘
              ┌──────────────┼──────────────┐
              │              │              │
        ┌─────▼────┐  ┌─────▼────┐  ┌─────▼────┐
        │ 发推服务  │  │ 时间线   │  │ 关注服务  │
        │          │  │ 服务     │  │          │
        └─────┬────┘  └─────┬────┘  └─────┬────┘
              │              │              │
        ┌─────▼────────────▼────────────▼─────┐
        │            消息队列                   │
        └─────────────┬───────────────────────┘
         ┌────────────┼────────────┐
         │            │            │
   ┌─────▼────┐ ┌────▼─────┐ ┌───▼──────┐
   │   MySQL  │ │  Redis   │ │ 对象存储  │
   │  (推文)  │ │ (时间线) │ │  (媒体)  │
   └──────────┘ └──────────┘ └──────────┘

1.3 Step 3:详细设计(15-20分钟)

核心目标:深入设计1-2个核心模块

不要试图详细设计每一个模块!面试官通常会指定深入的方向。

常见深入方向: - 数据模型设计(Schema设计) - 核心算法(如Feed排序、消息路由) - 缓存策略(缓存什么、如何更新) - 数据分片方案

数据模型示例

SQL
-- 用户表
CREATE TABLE users (
    user_id     BIGINT PRIMARY KEY,
    username    VARCHAR(32) UNIQUE,
    email       VARCHAR(128),
    created_at  TIMESTAMP
);

-- 推文表
CREATE TABLE tweets (
    tweet_id    BIGINT PRIMARY KEY,  -- Snowflake ID
    user_id     BIGINT,
    content     VARCHAR(280),
    media_url   VARCHAR(512),
    created_at  TIMESTAMP,
    INDEX idx_user_created (user_id, created_at DESC)
);

-- 关注关系表
CREATE TABLE follows (
    follower_id BIGINT,
    followee_id BIGINT,
    created_at  TIMESTAMP,
    PRIMARY KEY (follower_id, followee_id),
    INDEX idx_followee (followee_id)
);

1.4 Step 4:扩展优化(5-10分钟)

核心目标:讨论如何应对更大规模和特殊场景

常见讨论方向

方向 具体内容
扩展性 用户从100万增长到10亿怎么办?
可靠性 某个组件挂了怎么办?SPOF分析
热点问题 明星发推如何应对?
监控 关键指标有哪些?如何告警?
安全 如何防止恶意刷接口?
成本 有什么降低成本的方案?

2. 需求分析

2.1 功能性需求 vs 非功能性需求

Text Only
需求分类
├── 功能性需求(Functional Requirements)
│   ├── 系统"做什么"
│   ├── 用户可见的功能
│   └── 例:发帖、搜索、支付
└── 非功能性需求(Non-Functional Requirements)
    ├── 系统"做得多好"
    ├── 质量属性
    └── 例:性能、可用性、安全性

非功能性需求详解

需求 描述 量化指标示例
性能(Performance) 响应时间、吞吐量 P99 < 200ms, 10K QPS
可用性(Availability) 正常服务时间比例 99.99% uptime
可扩展性(Scalability) 处理增长的能力 支持10x流量增长
一致性(Consistency) 数据正确性保证 强一致/最终一致
持久性(Durability) 数据不丢失 99.999999999%(11个9)
安全性(Security) 防攻击能力 加密、认证、授权
可维护性(Maintainability) 修改和扩展的难度 模块化、可测试

2.2 需求优先级排序

面试中,需要快速确定需求优先级:

Text Only
P0(必须满足):核心功能 + 数据一致性/持久性
P1(应该满足):性能要求 + 可用性
P2(可以later):可扩展性 + 高级功能
P3(锦上添花):监控、分析、A/B测试

示例 — 电商系统需求优先级

优先级 需求 说明
P0 商品浏览、下单、支付 核心业务功能
P0 库存不能超卖 数据一致性
P0 支付数据不丢失 数据持久性
P1 页面加载 < 2秒 用户体验
P1 99.99% 可用性 业务连续性
P2 支持促销活动10x流量 可扩展性
P2 推荐系统 高级功能
P3 用户行为分析 运营需求

3. 容量估算

容量估算是系统设计的基础技能,用数据驱动架构决策。

3.1 关键数字速查表

基础数据

指标 数值 备注
秒/天 86,400 ≈ 100,000 方便计算取10万
秒/月 ≈ 2,500,000 ≈ 250万
1 KB 1,000 bytes 一条推文约
1 MB 10^6 bytes 一张高清图片约
1 GB 10^9 bytes 一部高清电影约
1 TB 10^12 bytes
1 PB 10^15 bytes

延迟数字(每个工程师都该知道的):

操作 延迟 量级
L1 cache访问 0.5 ns 纳秒级
L2 cache访问 7 ns 纳秒级
内存访问 100 ns 纳秒级
SSD随机读 150 μs 微秒级
HDD随机读 10 ms 毫秒级
同机房网络往返 0.5 ms 毫秒级
跨城市网络往返 30-100 ms 毫秒级
跨大洲网络往返 100-300 ms 百毫秒级

3.2 QPS估算方法

公式

\[QPS = \frac{DAU \times 每用户每日操作次数}{86400}\]
\[峰值QPS = QPS \times 峰值因子(通常2-5倍)\]

实战示例 — Twitter QPS估算

Text Only
已知条件:
  DAU = 3亿
  每人每天平均看20条推文
  每人每天平均发0.5条推文
  读写比 = 40:1

读QPS:
  = 3亿 × 20 / 86,400
  = 6,000,000,000 / 86,400
  ≈ 70,000 QPS

峰值读QPS:
  = 70,000 × 3(峰值因子)
  ≈ 210,000 QPS

写QPS:
  = 3亿 × 0.5 / 86,400
  ≈ 1,750 QPS

峰值写QPS:
  = 1,750 × 3
  ≈ 5,250 QPS

3.3 存储估算方法

公式

\[每日新增存储 = 每日新增数据条数 \times 每条数据大小\]
\[总存储 = 每日新增存储 \times 保存天数 \times 冗余因子\]

实战示例 — Twitter 存储估算

Text Only
推文存储:
  每日新增推文 = 3亿 × 0.5 = 1.5亿条
  每条推文大小 ≈ 400 bytes(含元数据)

  每日新增 = 1.5亿 × 400B = 60GB/天
  每年 = 60GB × 365 = 21.9TB/年

媒体存储(假设10%的推文含图片):
  每日含图推文 = 1500万条
  每张图片 ≈ 500KB

  每日新增 = 1500万 × 500KB = 7.5TB/天
  每年 = 7.5TB × 365 = 2.7PB/年

总存储(3年,含3份副本):
  推文:21.9TB × 3年 × 3副本 ≈ 200TB
  媒体:2.7PB × 3年 × 3副本 ≈ 24PB

3.4 带宽估算方法

公式

\[入站带宽 = 写QPS \times 每次写入数据大小\]
\[出站带宽 = 读QPS \times 每次读取数据大小\]

实战示例

Text Only
出站带宽(读推文):
  = 70,000 QPS × 400B
  = 28MB/s
  ≈ 224Mbps

出站带宽(读图片,假设30%请求带图):
  = 70,000 × 30% × 500KB
  = 10.5GB/s
  ≈ 84Gbps

  → 这个数据告诉我们:图片必须走CDN!

3.5 容量估算模板

Text Only
┌─────────────────────────────────────────────┐
│              容量估算模板                      │
├─────────────────────────────────────────────┤
│ 1. 用户规模                                  │
│    DAU: _______ 万                           │
│    MAU: _______ 万                           │
│    同时在线: _______ 万                       │
│                                              │
│ 2. 读写量                                    │
│    读QPS: _______ / 峰值: _______            │
│    写QPS: _______ / 峰值: _______            │
│    读写比: _______:1                          │
│                                              │
│ 3. 存储                                      │
│    单条数据大小: _______ bytes                │
│    每日新增: _______ GB                       │
│    保存期限: _______ 年                       │
│    总存储(含副本): _______ TB                 │
│                                              │
│ 4. 带宽                                      │
│    入站: _______ Mbps                        │
│    出站: _______ Gbps                        │
│                                              │
│ 5. 结论                                      │
│    需要 _______ 台应用服务器                  │
│    需要 _______ 台数据库服务器                │
│    是否需要CDN: □是 □否                       │
│    是否需要缓存: □是 □否                      │
│    是否需要消息队列: □是 □否                  │
└─────────────────────────────────────────────┘

4. CAP定理

4.1 CAP定理概述

CAP定理(Brewer定理)指出,一个分布式系统最多只能同时满足以下三项中的两项:

Text Only
            C (Consistency)
           一致性
            ╱ ╲
           ╱   ╲
          ╱     ╲
         ╱  只能  ╲
        ╱  选择两个 ╲
       ╱             ╲
      A ─────────────── P
 (Availability)    (Partition
   可用性          Tolerance)
                   分区容错性

三个属性的定义

属性 定义 直白理解
一致性 C 所有节点在同一时间看到同样的数据 写入后立即读到最新值
可用性 A 每个请求都能收到非错误响应 每个请求都有返回值
分区容错 P 网络分区时系统继续运行 网络断了系统不能直接挂

4.2 为什么是"三选二"?

在分布式系统中,网络分区(P)是一定会发生的(网线会断、交换机会故障),所以P是必选的。

真正的选择是:CP 还是 AP

Text Only
网络分区发生时:

CP系统的选择:                  AP系统的选择:
┌─────────────┐                ┌─────────────┐
│  保证一致性   │                │  保证可用性   │
│  放弃可用性   │                │  放弃一致性   │
│             │                │             │
│ 返回错误或    │                │ 返回可能过时  │
│ 等待恢复     │                │ 的数据       │
└─────────────┘                └─────────────┘

4.3 CP vs AP 系统对比

维度 CP系统 AP系统
优先保证 数据一致性 服务可用性
网络分区时 拒绝服务/等待 返回可能过时的数据
适用场景 金融交易、库存管理 社交媒体、内容推荐
用户感受 偶尔不可用但数据准确 总是可用但可能有延迟
典型系统 ZooKeeper、Etcd、HBase Cassandra、DynamoDB、CouchDB
一致性模型 线性一致性/强一致 最终一致

4.4 实际系统中的CAP选择

CP系统示例 — 银行转账

Text Only
场景:A向B转账100元

CP选择(正确):
  网络分区时 → 拒绝交易 → 宁可服务不可用,也不能出现A扣了钱B没收到

AP选择(危险):
  网络分区时 → 继续交易 → 可能导致A的钱扣了但B没收到,数据不一致

AP系统示例 — 朋友圈

Text Only
场景:用户发了一条朋友圈

AP选择(合理):
  网络分区时 → 北京用户先看到,上海用户1秒后看到
  结果:最终一致,用户体验可接受

CP选择(不必要):
  网络分区时 → 所有人都看不到 → 过度设计

4.5 CAP的常见误解

误解1:CAP意味着平时也只能选两个 ✅ 事实:CAP只在网络分区时才需要做出选择,正常情况下三者可以同时满足

误解2:选了CP就完全没有可用性 ✅ 事实:CP系统在正常情况下也是高可用的,只在分区时牺牲可用性

误解3:CAP中的C就是ACID的C ✅ 事实:CAP的C是线性一致性,ACID的C是事务一致性,是不同概念


5. BASE理论

5.1 BASE是什么

BASE是对CAP中AP方向的进一步阐释,是大规模分布式系统的实践指导:

缩写 全称 含义
BA Basically Available 基本可用:允许损失部分功能保证核心可用
S Soft State 软状态:允许数据存在中间状态(不同节点不一致)
E Eventually Consistent 最终一致:经过一段时间后数据最终达到一致

5.2 BASE vs ACID

Text Only
ACID(传统关系数据库)           BASE(分布式系统)
┌─────────────────┐            ┌─────────────────┐
│ Atomicity  原子性 │            │ Basically        │
│ Consistency 一致性│            │ Available 基本可用│
│ Isolation  隔离性 │            │ Soft State 软状态│
│ Durability 持久性 │            │ Eventually       │
│                  │            │ Consistent最终一致│
├─────────────────┤            ├─────────────────┤
│ 强一致性          │            │ 弱一致性         │
│ 优先保证正确性     │            │ 优先保证可用性    │
│ 适合小规模、      │            │ 适合大规模、      │
│ 对一致性要求高     │            │ 对可用性要求高    │
└─────────────────┘            └─────────────────┘

5.3 最终一致性的实现方式

方式 说明 延迟 适用场景
读时修复 读取时检测不一致并修复 取决于读取时机 Cassandra
写时修复 写入时同步到其他节点 写入延迟 主从复制
反熵 后台进程定期同步 秒级~分钟级 S3
Gossip协议 节点间随机传播 秒级 Cassandra/Redis Cluster

5.4 最终一致性的时间窗口

Text Only
写入                  最终一致
  │                     │
  ▼                     ▼
  ┌──────────────────────┐
  │    不一致时间窗口      │
  │                      │
  │  节点A: new_value    │
  │  节点B: old_value    │
  │  节点C: old_value    │
  │         ↓            │
  │  节点A: new_value    │
  │  节点B: new_value    │
  │  节点C: old_value    │
  │         ↓            │
  │  节点A: new_value    │
  │  节点B: new_value    │
  │  节点C: new_value    │ ← 达到一致
  └──────────────────────┘

  通常在毫秒~秒级内达到一致

6. 扩展策略

6.1 垂直扩展 vs 水平扩展

Text Only
垂直扩展 (Scale Up)              水平扩展 (Scale Out)
┌─────────────┐                ┌───┐ ┌───┐ ┌───┐
│             │                │   │ │   │ │   │
│             │                │   │ │   │ │   │
│  更强的机器  │                │   │ │   │ │   │
│             │                └───┘ └───┘ └───┘
│             │                ┌───┐ ┌───┐ ┌───┐
│             │                │   │ │   │ │   │
│             │                │   │ │   │ │   │
│             │                └───┘ └───┘ └───┘
└─────────────┘                更多的普通机器

全面对比

维度 垂直扩展 (Scale Up) 水平扩展 (Scale Out)
方式 升级单机硬件 增加更多机器
成本 指数增长 线性增长
上限 有物理上限 理论无上限
复杂度 简单,无需改架构 复杂,需要处理分布式问题
可用性 单点故障风险 天然支持冗余
数据一致性 简单(单机) 复杂(需要分布式一致性)
适用阶段 初期/中小规模 大规模
典型方案 加CPU/内存/SSD 加服务器 + 负载均衡
示例 MySQL升级到更大的机器 MySQL分库分表到多台机器

6.2 什么时候选择哪种?

决策流程

Text Only
当前系统是否到了瓶颈?
    ├── 否 → 不需要扩展
    └── 是 → 垂直扩展是否还有空间?
              ├── 是 → 先垂直扩展(成本低、风险小)
              │        └── 但要同时预研水平扩展方案
              └── 否 → 水平扩展
                       ├── 无状态服务 → 加机器 + LB
                       ├── 有状态服务 → 需要考虑分片/复制
                       └── 数据层 → 分库分表/读写分离

6.3 各层扩展策略

层次 垂直扩展 水平扩展
Web层 升级服务器配置 多实例 + 负载均衡
应用层 升级CPU/内存 无状态化 + 多实例
缓存层 升级内存 分布式缓存集群(Redis Cluster)
数据库层 升级SSD/内存 读写分离 + 分库分表
存储层 升级磁盘 分布式文件系统

6.4 扩展性设计原则

  1. 无状态化:应用服务器不存储会话数据,使用外部存储(Redis)
  2. 松耦合:服务之间通过消息队列解耦
  3. 异步化:非实时操作使用异步处理
  4. 幂等性:接口设计保证重试安全
  5. 分而治之:数据分片、服务拆分
Text Only
有状态服务(难扩展)          无状态服务(易扩展)
┌──────────────┐            ┌──────────────┐
│   Server A   │            │   Server A   │
│ session: {..}│            │   无状态      │──┐
└──────────────┘            └──────────────┘  │
     ❌ 用户只能              ┌──────────────┐  │  ┌────────┐
     访问同一台机器            │   Server B   │──┼──│ Redis  │
                             │   无状态      │  │  │Session │
                             └──────────────┘  │  └────────┘
                             ┌──────────────┐  │
                             │   Server C   │──┘
                             │   无状态      │
                             └──────────────┘
                             ✅ 任何机器都可处理

7. 设计原则与思维模型

7.1 核心设计原则

单一职责原则(SRP): - 每个服务只负责一个业务域 - 好处:独立开发、独立部署、独立扩展

80/20原则: - 80%的请求访问20%的数据 - 用于指导缓存策略:缓存热数据

就近原则: - 数据和计算尽量靠近用户 - CDN、缓存、就近节点

最终一致原则: - 不是所有数据都需要强一致 - 区分哪些需要强一致(交易),哪些可以最终一致(社交)

7.2 Trade-off 思维框架

系统设计的核心是Trade-off。每个决策都有利弊,面试中最重要的是展示你的思考过程。

常见Trade-off

取舍 选择A 选择B 如何决策
一致性 vs 可用性 强一致(CP) 高可用(AP) 看业务(金融vs社交)
延迟 vs 吞吐 低延迟(同步) 高吞吐(批处理) 看使用场景
读优化 vs 写优化 读多写少(缓存+副本) 写多读少(LSM-Tree) 看读写比
简单 vs 高性能 单体架构 微服务 看团队和规模
成本 vs 性能 省钱(最终一致) 高性能(强一致) 看预算和SLA
灵活性 vs 一致性 NoSQL(Schema灵活) SQL(Schema固定) 看数据特征

Trade-off表达模板(面试中使用):

Text Only
"在这里,我们有两个选择:A和B。

A的优势是___,劣势是___。
B的优势是___,劣势是___。

考虑到我们的场景(具体需求),我倾向于选择A,
因为___。

但如果未来需求变化(具体变化),我们可能需要迁移到B。"

7.3 Back-of-the-Envelope Estimation

快速心算技巧

简化 精确值 误差
1天 ≈ 10^5 秒 86,400秒 15%
1年 ≈ 3 × 10^7 秒 31,536,000秒 5%
2^10 ≈ 10^3 1,024 2%
2^20 ≈ 10^6 1,048,576 5%
2^30 ≈ 10^9 1,073,741,824 7%

2的幂次速查

Text Only
2^10 = 1,024          ≈ 千 (K)
2^20 = 1,048,576      ≈ 百万 (M)
2^30 = 1,073,741,824  ≈ 十亿 (G)
2^40 = 1,099,511,627,776 ≈ 万亿 (T)

8. 练习与延伸阅读

8.1 练习题

容量估算练习

  1. 微信消息存储
  2. DAU: 12亿,每人每天发50条消息,每条消息平均100字节
  3. 计算日新增存储量和年存储量

    日: 12\u00d710\u2079 \u00d7 50 \u00d7 100B = 6TB/天。年: 6TB \u00d7 365 \u2248 2.2PB/年。加上元数据和索引约 \u00d73 \u2248 6.6PB/年。

  4. YouTube视频存储

  5. DAU: 20亿,每天上传50万个视频,平均每个视频300MB
  6. 计算日新增存储量和所需带宽

    日: 50万 \u00d7 300MB = 150TB/天。带宽(均匀分布): 150TB/86400s \u2248 14Gbps 写入。如果考虑多分辨率转码(\u00d75) \u2248 750TB/天存储。

  7. 淘宝商品搜索

  8. DAU: 8亿,每人每天搜索5次,每次返回20个结果
  9. 计算搜索QPS和峰值QPS

    日均QPS: 8\u00d710\u2078 \u00d7 5 / 86400 \u2248 46K QPS。峰值(\u00d73): \u2248 140K QPS。每秒返回结果: 140K \u00d7 20 = 280万条/秒。

CAP分析练习

  1. 分析以下系统应该选择CP还是AP,并说明理由:
  2. 12306火车票订票系统 > CP。余票数量必须强一致(不能超卖),宁可暂时不可用也不能数据不一致。
  3. 微博热搜排行榜 > AP。排行榜短暂不一致可接受(几秒延迟无所谓),高可用更重要(不能宕机)。
  4. 支付宝转账系统 > CP。金融交易必须强一致(不能多扣/少扣),可以牺牲短暂可用性(转账延迟用户可接受)。
  5. B站弹幕系统 > AP。弹幕丢失几条或短暂不同步用户几乎无感知,但服务不可用会严重影响体验。

设计练习

  1. 用4步法设计一个"知乎"系统(限时45分钟)
  2. 用4步法设计一个"网盘"系统(限时45分钟)

8.2 延伸阅读

  • 《DDIA》第1章 —— 可靠性、可扩展性、可维护性
  • 《System Design Interview》第2章 —— Back-of-the-envelope Estimation
  • Jeff Dean: "Numbers Everyone Should Know"
  • Martin Fowler: "CAP Twelve Years Later" 论文
  • Eric Brewer: "CAP定理" 原始论文

📝 本章小结

知识点 关键要点
4步法 需求澄清 → 高层设计 → 详细设计 → 扩展优化
需求分析 功能性(做什么) vs 非功能性(做得多好)
容量估算 QPS/存储/带宽,用数据驱动决策
CAP定理 P必选,实际选CP或AP
BASE 基本可用 + 软状态 + 最终一致
扩展策略 优先垂直扩展,上限后水平扩展
Trade-off 没有完美方案,说清选择的理由

上一章:学习指南 | 下一章:核心组件详解