系统设计

「面试官问:如果要设计一个秒杀系统,你怎么入手?」

大多数人第一反应是画架构图:网关、缓存、消息队列、数据库——看起来面面俱到。但面试官追问几个细节就卡住了:库存怎么扣?超卖怎么避免?热 key 怎么应对?削峰填谷用消息队列还是本地队列?每个问题背后都是一个深坑。

系统设计的能力,本质上是一种权衡决策能力。不存在完美的架构,只有在特定约束下的最优解。你需要在一致性 vs 可用性、延迟 vs 吞吐量、复杂度 vs 可维护性之间做出取舍。理解这些权衡,比记住某个具体问题的标准答案更重要。

本分类聚焦系统设计的核心知识体系,从设计方法论出发,深入讲解权衡分析、扩展策略、数据分片、缓存策略、负载均衡、消息与流系统、存储引擎等核心领域——帮助你建立「面对未知问题,能从第一性原理出发,做出合理设计决策」的能力。

模块结构

本分类按主题分为 8 个子模块:

子模块核心问题典型场景
设计方法论怎么从零开始设计一个系统?面试框架、需求分析、容量规划
权衡分析每个技术选型背后的 trade-off 是什么?CAP/PACELC、一致性选型、架构风格选择
扩展策略系统怎么从单机扩展到多机?垂直扩展、水平扩展、读写分离
数据分片数据怎么分不到多个节点?一致性哈希、分片键设计、跨分片查询
缓存策略缓存怎么用才能真正提升性能?穿透/击穿/雪崩、布隆过滤器、多级缓存
负载均衡流量怎么分发到多个节点?L4/L7 负载均衡、一致性哈希、会话保持
消息与流系统异步消息怎么保证可靠?Kafka 分区、RocketMQ 事务消息、Exactly-Once
存储引擎数据怎么存储才能既快又稳?B+ Tree、LSM Tree、InnoDB、RocksDB

系统设计的核心矛盾

系统设计的所有问题,本质上都围绕着三对核心矛盾的权衡:

flowchart TD
    A["系统设计核心矛盾"] --> B["一致性与可用性"]
    A --> C["延迟与吞吐量"]
    A --> D["复杂度与可维护性"]

    B --> B1["CAP 定理"]
    B --> B2["PACELC 定理"]
    B --> B3["强一致 vs 最终一致"]

    C --> C1["同步 vs 异步"]
    C --> C2["本地 vs 分布式"]
    C --> C3["批处理 vs 流处理"]

    D --> D1["单体 vs 微服务"]
    D --> D2["自研 vs 使用成熟中间件"]
    D --> D3["功能完整 vs 快速迭代"]

一致性 vs 可用性:CAP 定理告诉我们,在网络分区发生时,一致性和可用性只能二选一。但 PACELC 更进一步:即使没有网络分区,一致性和延迟之间也存在权衡。

延迟 vs 吞吐量:低延迟和高吞吐量往往不可兼得。追求低延迟通常意味着更多资源投入(如预热缓存),追求高吞吐量通常意味着接受一定延迟(如批量处理)。

复杂度 vs 可维护性:越复杂的系统,能解决的问题越多,但出问题的概率也越大,运维成本也越高。架构选型的关键在于判断当前阶段的「合适复杂度」。

设计方法论:从问题到方案

系统设计不是一上来就画架构图,而是从理解问题开始。

第一步:需求分析

功能需求 vs 非功能需求。功能需求回答「系统做什么」,非功能需求回答「系统做得怎么样」。

非功能需求通常包括:

  • 性能:QPS/TPS、延迟(p50/p90/p99)
  • 可用性:多少个 9、容灾要求
  • 扩展性:预期数据量、用户规模、增长速率
  • 一致性:强一致还是最终一致、可接受的延迟
  • 成本:预算约束、人力约束

常见错误:上来就关注「用什么技术」,而忽略了「业务真正需要什么」。很多系统设计失败,不是因为技术选错了,而是因为没有充分理解需求。

第二步:容量规划

在动手设计之前,先估算规模:

flowchart TD
    A["容量规划"] --> B["QPS 估算"]
    A --> C["数据量估算"]
    A --> D["存储容量估算"]
    A --> E["带宽估算"]

    B --> B1["DAU × 人均请求数\n÷ 每日秒数"]
    B1 --> B2["峰值 QPS = 平均 QPS × 倍数"]

    C --> C1["用户数 × 每用户数据量\n× 增长系数"]
    C1 --> C2["数据量年增长预估"]

    D --> D1["数据量 ÷ 压缩比\n× 副本系数\n÷ 存储利用率"]
    D1 --> D2["选择存储方案"]

    E --> E1["QPS × 平均响应大小\n× 8 ÷ 1000"]
    E1 --> E2["选择网络方案"]

第三步:高层设计

从整体架构入手,不要一开始就陷入细节。

典型的高层设计流程:

  1. 画出核心组件:前端、网关、业务服务、数据存储
  2. 确定数据流:请求从哪里进、数据往哪里走
  3. 识别瓶颈点:可能的单点和热点
  4. 制定扩展策略:先抗住,再优化

第四步:细节设计

针对核心问题深入设计。每个系统都有几个「hard parts」,这些是需要重点突破的地方:

flowchart LR
    subgraph 常见hardparts["常见 Hard Parts"]
        A["一致性保证\n库存扣减、余额扣款"]
        B["高并发热点\n热 key、热点数据"]
        C["数据完整性\n幂等、去重、防超卖"]
        D["可扩展性\n分片、迁移、扩容"]
    end

核心问题域

数据层

数据层是大多数系统最难的部分。当数据量超过单机容量时,一切都会变得复杂:

flowchart TD
    A["数据量增长"] --> B{"单机够用吗?"}
    B -->|"是"| C["优化索引\n读写分离"]
    B -->|"否"| D["数据分片"]
    D --> D1["范围分片"]
    D --> D2["哈希分片"]
    D --> D3["一致性哈希"]
    C --> E["继续观察"]
    D1 --> E
    D2 --> E
    D3 --> E
    E --> F["数据量再增长"]
    F --> D

每一步扩展都会引入新的复杂度:分片后跨分片查询怎么解决?分片键选错了怎么办?节点扩缩容时数据怎么迁移?这些问题在没有实际压力之前,往往被忽视。

缓存层

缓存是性能优化的利器,但也是生产事故的高发区:

flowchart TD
    A["加缓存"] --> B["命中率够吗?"]
    B -->|"不够"| C["优化缓存策略"]
    B -->|"够"| D["三个经典问题"]
    C --> D
    D --> E1["缓存穿透\n布隆过滤器"]
    D --> E2["缓存击穿\n互斥锁/逻辑过期"]
    D --> E3["缓存雪崩\n随机 TTL/多级缓存"]
    E1 --> F["一致性保证\n延迟双删/订阅 binlog"]
    E2 --> F
    E3 --> F

常见误区:加了缓存性能就提升了。实际上,如果缓存命中率过低,缓存反而增加开销;如果缓存和数据库的一致性没做好,可能导致更严重的数据问题。

消息层

消息队列解决了异步化和削峰的问题,但引入了新的复杂度:

  • 消息可靠性:消息会不会丢?怎么保证?
  • 消息顺序:消息消费的顺序能否保证?
  • 消息重复:At-Least-Once 语义下如何做到 Exactly-Once?
  • 消息积压:消费者处理不过来怎么办?

这些问题的根源在于:消息队列是异步的,而业务逻辑通常是同步的。异步通信的复杂性远高于同步通信。

存储层

存储引擎的选择往往是最难回头的决策:

flowchart TD
    A["选存储引擎"] --> B{"写入模式?"}
    B -->|"顺序写入为主"| C["LSM Tree\nRocksDB/LevelDB"]
    B -->|"随机读写为主"| D["B+ Tree\nInnoDB"]
    B -->|"列式分析为主"| E["列式存储\nClickHouse/Parquet"]
    C --> F["写放大问题"]
    D --> G["读放大问题"]
    E --> H["聚合查询优化"]

LSM Tree 适合写多读少的场景(如日志、消息),B+ Tree 适合读多写少的场景(如业务库)。选错了存储引擎,后续迁移成本极高。

各子模块导读

设计方法论

如果你不知道拿到一个系统设计题怎么入手,从这里开始。

必读系统设计面试流程与框架——四步法:需求分析、高层设计、细节设计、权衡分析;需求分析——功能需求 vs 非功能需求;容量规划——QPS/TPS 怎么估算。

推荐延迟估算——数字速算表,快速估算各组件延迟量级;存储容量估算——数据量、存储容量、备份策略;系统设计文档规范——如何写一份合格的设计文档。

权衡分析

这是整个分类的「思想基础」,帮助你理解为什么设计决策没有标准答案。

必读CAP 定理与 PACELC——分布式系统不可能三角;一致性选择矩阵——强一致 vs 最终一致的使用场景;延迟 vs 吞吐量——两者为什么往往不可兼得。

推荐同步 vs 异步SQL vs NoSQL 选型矩阵单体 vs 微服务状态ful vs 状态less

选读读优化 vs 写优化推模式 vs 拉模式成本 vs 性能

扩展策略

如果你在思考「系统扛不住了怎么办」,从这里开始。

核心概念AKF 扩展立方体——X/Y/Z 三轴扩展的理论基础;无状态化设计——可扩展的前提;水平扩展——最常用的扩展方式。

具体策略垂直扩展(Scale Up)X 轴扩展:水平复制Y 轴扩展:功能拆分Z 轴扩展:数据分区读写分离扩展计算与存储分离弹性伸缩(Auto-scaling)

数据分片

如果数据量超过单机容量,从这里开始。

核心概念分片策略总览——范围/哈希/一致性哈希的选择;一致性哈希原理——环、分片、虚拟节点;分片键设计——选错分片键的代价。

分片策略范围分片哈希分片一致性哈希虚拟节点与负载均衡目录分片地理分片

进阶问题热点数据处理动态分片与重平衡分片带来的查询挑战分片与二级索引分片 vs 分区

缓存策略

缓存是性能优化最有效的手段之一,也是最容易踩坑的地方。

入门缓存系统概述与收益分析——缓存的本质与收益评估;本地缓存——Caffeine/Guava Cache;分布式缓存——Redis/Memcached。

三大问题缓存穿透缓存击穿缓存雪崩布隆过滤器——解决缓存穿透的利器。

进阶多级缓存架构缓存一致性写穿/写回/写旁路延迟双删缓存预热与刷新缓存淘汰策略缓存监控与指标

负载均衡

流量分发的核心机制,理解它是理解分布式系统的基础。

核心概念负载均衡概述四层负载均衡(LVS/DPVS)七层负载均衡(Nginx/HAProxy)

算法轮询与加权轮询最小连接数最短响应时间IP Hash 与一致性哈希地理位置负载均衡(GSLB)

实践客户端负载均衡服务端负载均衡健康检查机制会话保持(Sticky Session)全局负载均衡与灾备

消息与流系统

异步通信的核心,理解它才能设计出真正可扩展的系统。

核心概念消息队列核心概念点对点 vs 发布订阅模型消息顺序保证消息可靠性与确认机制死信队列与重试队列消息积压处理策略

进阶Exactly-Once 语义实现流处理 vs 批处理消息队列在微服务中的应用

中间件对比Kafka 架构深度解析Kafka 分区与消费者组Pulsar 架构深度解析RabbitMQ 架构深度解析RocketMQ 架构深度解析消息队列选型对比矩阵

存储引擎

理解数据是如何被持久化的,才能做出正确的存储选型。

核心概念存储引擎概述WAL 预写日志机制

LSM Tree 系列LSM Tree 原理详解SSTable 与 MemTableLSM Tree 的 Compaction 策略RocksDB 架构解析LevelDB 架构解析Bloom Filter 在存储引擎中的应用

B+ Tree 系列B+ Tree 原理详解InnoDB 存储引擎架构

对比与选型B+ Tree 与 LSM Tree 对比列式存储 vs 行式存储位图索引与倒排索引存储引擎选型指南

学习路线建议

flowchart LR
    A["阶段一\n建立框架"] --> B["阶段二\n攻数据层"]
    B --> C["阶段三\n攻性能层"]
    C --> D["阶段四\n攻可靠性"]
    D --> E["阶段五\n综合实战"]

    A --> A1["设计方法论"]
    A1 --> A2["权衡分析方法论"]
    A2 --> A3["容量规划"]

    B --> B1["数据分片策略"]
    B1 --> B2["存储引擎原理"]
    B2 --> B3["读写分离与扩展"]

    C --> C1["缓存三大问题"]
    C1 --> C2["负载均衡算法"]
    C2 --> C3["I/O 与存储优化"]

    D --> D1["消息队列原理"]
    D1 --> D2["消息可靠性保证"]
    D2 --> D3["分布式一致性"]

    E --> E1["秒杀系统设计"]
    E1 --> E2["短链接系统设计"]
    E2 --> E3["Feed 流系统设计"]

面试速成路线(目标:应对系统设计面试):

设计方法论 → 容量规划 → 数据分片 → 缓存策略 → 消息队列 → 经典问题(秒杀、Feed流、URL短链)

工程实践路线(目标:实际项目设计):

权衡分析方法论 → 扩展策略 → 存储引擎原理 → 缓存一致性 → 消息可靠性 → 分片键设计

深度原理路线(目标:成为系统设计专家):

存储引擎原理 → 一致性协议 → 分布式事务 → 性能优化深度 → 架构演进案例

准备好了吗?让我们从系统设计的方法论开始,建立正确的设计思维。