feat: add comprehensive backend topics and fix build issues
## 新增内容 ### 附录文档扩展 - 扩展前端项目架构文档 (frontend-project-architecture.md) - 扩展后端项目架构文档 (backend-project-architecture.md) - 扩展数据治理文档 (data-governance.md) - 扩展数据可视化文档 (data-visualization.md) - 扩展分布式系统文档 (distributed-systems.md) - 扩展高可用文档 (high-availability.md) - 扩展单体到微服务文档 (monolith-to-microservices.md) - 扩展系统设计方法论文档 (system-design-methodology.md) - 扩展 Docker 容器文档 (docker-containers.md) - 扩展 Kubernetes 文档 (kubernetes.md) - 扩展 Linux 基础文档 (linux-basics.md) - 扩展神经网络文档 (neural-networks.md) ### 新增交互式组件 - 数据治理组件: DataQualityDemo, DataGovernanceFrameworkDemo, DataLineageDemo - 数据可视化组件: ChartTypeSelectorDemo, DashboardLayoutDemo - 分布式系统组件: CAPTheoremDemo, ConsistencyModelsDemo, DistributedChallengesDemo - 高可用组件: AvailabilityCalculatorDemo, FailoverStrategyDemo - 系统设计组件: SystemDesignStepsDemo, CapacityEstimationDemo - Docker 容器组件: DockerArchitectureDemo, DockerLifecycleDemo - Kubernetes 组件: K8sArchitectureDemo, K8sWorkloadsDemo - Linux 基础组件: LinuxFileSystemDemo, LinuxCommandDemo, LinuxPermissionsDemo - 神经网络组件: NeuronDemo, NetworkLayersDemo, NetworkArchitectureDemo - 单体到微服务组件: ArchEvolutionDemo - 项目架构组件: ProjectArchitectureComparisonDemo - 附录导航组件: AppendixFlowMap ### 英文版重构 - 将 en-us 目录重命名为 en - 更新相关配置和组件中的语言代码 ## Bug 修复 - 修复 index.js 中重复的组件导入语句导致的 build 失败 - 恢复被注释的 InvertedIndexDemo 和 SearchRelevanceDemo 导入 - 修复 HomeFeatures.vue 中 en-us 与 config.mjs 中 en 不一致导致的语言切换问题 ## 其他改进 - 添加构建脚本 (scripts/build.mjs) - 更新依赖版本
This commit is contained in:
@@ -1,3 +1,253 @@
|
||||
# 分布式系统的挑战
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**当一台机器不够用时,问题才真正开始。** 分布式系统是现代互联网的基石——从微信消息到淘宝下单,背后都是成百上千台机器协同工作。但"分布式"不是免费的午餐,它带来了一系列单机系统从未遇到的挑战。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **核心定理**:理解 CAP 定理及其对系统设计的影响
|
||||
- **一致性模型**:区分强一致性、最终一致性、因果一致性
|
||||
- **八大挑战**:掌握分布式系统面临的核心难题
|
||||
- **共识算法**:了解 Paxos、Raft 等分布式共识的基本思想
|
||||
- **实战模式**:熟悉 2PC、Saga、CRDT 等常用解决方案
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 为什么需要分布式 | 扩展性、可用性、地理分布 |
|
||||
| **第 2 章** | CAP 定理 | 一致性、可用性、分区容错 |
|
||||
| **第 3 章** | 一致性模型 | 强一致、最终一致、因果一致 |
|
||||
| **第 4 章** | 八大挑战 | 网络、时钟、分区、脑裂等 |
|
||||
| **第 5 章** | 共识算法 | Paxos、Raft、ZAB |
|
||||
| **第 6 章** | 分布式事务 | 2PC、Saga、TCC |
|
||||
|
||||
---
|
||||
|
||||
## 0. 全景图:为什么需要分布式系统?
|
||||
|
||||
单机系统简单可靠,但有三个无法逾越的瓶颈:
|
||||
|
||||
| 瓶颈 | 说明 | 分布式的解法 |
|
||||
|------|------|-------------|
|
||||
| 性能上限 | 单机 CPU、内存、磁盘有物理极限 | 水平扩展:加更多机器分担负载 |
|
||||
| 单点故障 | 一台机器挂了,整个服务就挂了 | 冗余副本:多台机器互为备份 |
|
||||
| 地理延迟 | 用户在全球各地,单机只能在一个地方 | 多地部署:就近服务用户 |
|
||||
|
||||
::: tip 分布式的代价
|
||||
分布式系统解决了上面的问题,但引入了新的复杂性:网络不可靠、时钟不同步、部分失败、数据一致性……这些就是本文要讨论的"挑战"。
|
||||
|
||||
**Peter Deutsch 的分布式计算八大谬误**告诉我们,以下假设在分布式环境中都是错的:
|
||||
1. 网络是可靠的
|
||||
2. 延迟是零
|
||||
3. 带宽是无限的
|
||||
4. 网络是安全的
|
||||
5. 拓扑不会变化
|
||||
6. 只有一个管理员
|
||||
7. 传输成本是零
|
||||
8. 网络是同构的
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 1. CAP 定理:分布式系统的"不可能三角"
|
||||
|
||||
2000 年,Eric Brewer 提出了 CAP 猜想(后被证明为定理):一个分布式系统最多只能同时满足以下三个属性中的两个。
|
||||
|
||||
| 属性 | 含义 | 通俗理解 |
|
||||
|------|------|---------|
|
||||
| **C**onsistency(一致性) | 所有节点在同一时刻看到相同的数据 | 你在任何 ATM 查余额,结果都一样 |
|
||||
| **A**vailability(可用性) | 每个请求都能收到非错误的响应 | 系统永远能回应你,不会说"服务不可用" |
|
||||
| **P**artition tolerance(分区容错) | 网络分区时系统仍能继续运行 | 即使部分网线断了,系统还能工作 |
|
||||
|
||||
<CAPTheoremDemo />
|
||||
|
||||
### 为什么只能选两个?
|
||||
|
||||
在分布式环境中,网络分区(P)是不可避免的——光纤会被挖断、交换机会故障、数据中心会断网。所以 P 是必选项,实际的选择是在 C 和 A 之间权衡:
|
||||
|
||||
- **选 CP**:分区时拒绝不确定的请求,保证数据正确 → 适合金融、库存
|
||||
- **选 AP**:分区时继续服务,但数据可能暂时不一致 → 适合社交、内容
|
||||
|
||||
::: tip CAP 不是非黑即白
|
||||
现实中的系统不是简单的"CP 或 AP"。很多系统在不同操作上做不同的选择——比如同一个数据库,读操作可以是 AP(允许读旧数据),写操作可以是 CP(要求多数确认)。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 一致性模型:数据同步的"严格程度"
|
||||
|
||||
一致性不是一个开关(有或没有),而是一个光谱。不同的一致性模型在"正确性"和"性能"之间做不同的权衡。
|
||||
|
||||
<ConsistencyModelsDemo />
|
||||
|
||||
### 一致性模型对比
|
||||
|
||||
| 模型 | 保证 | 延迟 | 适用场景 |
|
||||
|------|------|------|---------|
|
||||
| 强一致性 | 读到的一定是最新写入的值 | 高(需等待同步) | 银行转账、库存扣减 |
|
||||
| 最终一致性 | 最终所有副本会一致,但中间可能读到旧值 | 低(写入立即返回) | 社交动态、DNS |
|
||||
| 因果一致性 | 有因果关系的操作保证顺序 | 中等 | 评论回复、协作编辑 |
|
||||
| 线性一致性 | 所有操作看起来像在单机上按顺序执行 | 最高 | 分布式锁、选主 |
|
||||
| 会话一致性 | 同一会话内保证读到自己的写入 | 低-中 | 用户个人数据 |
|
||||
|
||||
::: tip "读己之写"一致性
|
||||
最常见的实际需求是:用户修改了自己的数据后,自己能立即看到更新(但其他用户可以稍后看到)。这叫"Read Your Own Writes"一致性,是最终一致性的一个实用增强。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 3. 八大挑战:分布式系统的"地雷阵"
|
||||
|
||||
分布式系统的复杂性不是来自某一个问题,而是多个问题交织在一起。以下是最核心的八大挑战。
|
||||
|
||||
<DistributedChallengesDemo />
|
||||
|
||||
### 挑战之间的关联
|
||||
|
||||
这八大挑战不是孤立的,它们相互关联:
|
||||
|
||||
- **网络不可靠** → 导致 **网络分区** → 触发 **CAP 权衡**
|
||||
- **时钟不同步** → 导致 **事件排序困难** → 影响 **数据一致性**
|
||||
- **部分失败** → 可能导致 **脑裂** → 需要 **共识算法** 来解决
|
||||
- **数据一致性** → 需要 **分布式事务** → 但事务又受 **网络不可靠** 影响
|
||||
|
||||
::: tip 没有银弹
|
||||
分布式系统没有"完美"的解决方案,只有"合适"的权衡。理解这些挑战的本质,才能在设计系统时做出正确的取舍。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 共识算法:如何让多台机器"达成一致"
|
||||
|
||||
共识算法是分布式系统的核心——它解决的问题是:多个节点如何就某个值达成一致?即使部分节点故障或网络延迟。
|
||||
|
||||
### 4.1 Paxos
|
||||
|
||||
Leslie Lamport 在 1990 年提出,是第一个被严格证明正确的共识算法。
|
||||
|
||||
| 角色 | 职责 |
|
||||
|------|------|
|
||||
| Proposer | 提出提案(值) |
|
||||
| Acceptor | 投票接受或拒绝提案 |
|
||||
| Learner | 学习最终被选定的值 |
|
||||
|
||||
**两阶段流程**:
|
||||
1. **Prepare 阶段**:Proposer 发送提案编号,Acceptor 承诺不再接受更小编号的提案
|
||||
2. **Accept 阶段**:Proposer 发送具体值,多数 Acceptor 接受则提案通过
|
||||
|
||||
::: tip Paxos 的问题
|
||||
Paxos 虽然正确,但出了名的难以理解和实现。Lamport 自己的论文用了一个希腊议会的比喻,结果让更多人困惑了。
|
||||
:::
|
||||
|
||||
### 4.2 Raft:为可理解性而生
|
||||
|
||||
2014 年 Diego Ongaro 提出 Raft,目标是做一个"容易理解的 Paxos"。它把共识问题分解为三个子问题:
|
||||
|
||||
| 子问题 | 说明 |
|
||||
|--------|------|
|
||||
| Leader 选举 | 集群中选出一个 Leader,所有写入都经过 Leader |
|
||||
| 日志复制 | Leader 将操作日志复制到所有 Follower |
|
||||
| 安全性 | 保证已提交的日志不会被覆盖 |
|
||||
|
||||
**Raft 的核心流程**:
|
||||
1. 集群启动时,所有节点都是 Follower
|
||||
2. 如果 Follower 超时没收到 Leader 心跳,就变成 Candidate 发起选举
|
||||
3. 获得多数票的 Candidate 成为新 Leader
|
||||
4. Leader 接收客户端请求,将日志复制到多数节点后提交
|
||||
|
||||
### 4.3 共识算法对比
|
||||
|
||||
| 算法 | 提出时间 | 可理解性 | 使用系统 |
|
||||
|------|---------|---------|---------|
|
||||
| Paxos | 1990 | 困难 | Google Chubby |
|
||||
| Raft | 2014 | 容易 | etcd、Consul、TiKV |
|
||||
| ZAB | 2011 | 中等 | ZooKeeper |
|
||||
| EPaxos | 2013 | 困难 | 学术研究为主 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 分布式事务:跨节点的"全有或全无"
|
||||
|
||||
单机数据库的事务靠本地锁和日志就能实现 ACID。但当一个业务操作涉及多个服务/数据库时,如何保证原子性?
|
||||
|
||||
### 5.1 两阶段提交(2PC)
|
||||
|
||||
最经典的分布式事务协议,分为两个阶段:
|
||||
|
||||
| 阶段 | 协调者动作 | 参与者动作 |
|
||||
|------|-----------|-----------|
|
||||
| Prepare | 问所有参与者"能提交吗?" | 执行操作但不提交,回复 Yes/No |
|
||||
| Commit | 如果全部 Yes,发送 Commit | 正式提交;如果有 No,全部回滚 |
|
||||
|
||||
**2PC 的问题**:
|
||||
- **阻塞**:Prepare 后如果协调者挂了,参与者会一直等待
|
||||
- **单点故障**:协调者是单点,挂了整个事务卡住
|
||||
- **性能差**:需要多次网络往返,锁持有时间长
|
||||
|
||||
### 5.2 Saga 模式
|
||||
|
||||
Saga 把一个大事务拆成多个本地事务,每个本地事务有对应的补偿操作。如果某一步失败,就逆序执行补偿。
|
||||
|
||||
**电商下单的 Saga 示例**:
|
||||
|
||||
| 步骤 | 正向操作 | 补偿操作 |
|
||||
|------|---------|---------|
|
||||
| T1 | 创建订单(待支付) | 取消订单 |
|
||||
| T2 | 扣减库存 | 恢复库存 |
|
||||
| T3 | 扣减余额 | 退还余额 |
|
||||
| T4 | 确认订单(已支付) | — |
|
||||
|
||||
如果 T3(扣减余额)失败:执行 C2(恢复库存)→ C1(取消订单)。
|
||||
|
||||
**两种编排方式**:
|
||||
- **编排式(Choreography)**:每个服务监听事件,自行决定下一步。简单但难以追踪全局状态
|
||||
- **协调式(Orchestration)**:有一个中心协调者控制流程。清晰但协调者是单点
|
||||
|
||||
### 5.3 TCC(Try-Confirm-Cancel)
|
||||
|
||||
TCC 是 2PC 的业务层实现,把每个操作分为三个阶段:
|
||||
|
||||
| 阶段 | 说明 | 示例(扣库存) |
|
||||
|------|------|---------------|
|
||||
| Try | 预留资源,但不真正执行 | 冻结 10 件库存(可用库存 -10,冻结库存 +10) |
|
||||
| Confirm | 确认执行,消耗预留资源 | 冻结库存 -10(真正扣减) |
|
||||
| Cancel | 取消预留,释放资源 | 冻结库存 -10,可用库存 +10(恢复) |
|
||||
|
||||
### 5.4 三种方案对比
|
||||
|
||||
| 方案 | 一致性 | 性能 | 复杂度 | 适用场景 |
|
||||
|------|--------|------|--------|---------|
|
||||
| 2PC | 强一致 | 低 | 中 | 数据库层面的跨库事务 |
|
||||
| Saga | 最终一致 | 高 | 高 | 长流程业务(订单、物流) |
|
||||
| TCC | 最终一致 | 中 | 最高 | 资金类高可靠场景 |
|
||||
|
||||
::: tip 实际选择建议
|
||||
- 能用单库事务就不要用分布式事务
|
||||
- 大多数业务场景用 Saga + 消息队列就够了
|
||||
- TCC 适合对一致性要求极高的金融场景,但开发成本很高
|
||||
- 2PC 适合数据库中间件(如 ShardingSphere)自动处理
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
分布式系统是现代互联网的基础设施,但它的复杂性远超单机系统。理解这些挑战不是为了"解决"它们(很多是根本性的),而是为了在设计系统时做出正确的权衡。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **CAP 定理**:网络分区不可避免,实际选择是在一致性和可用性之间权衡
|
||||
2. **一致性模型**:从强一致到最终一致是一个光谱,根据业务需求选择
|
||||
3. **八大挑战**:网络不可靠、时钟不同步、网络分区、脑裂等相互关联
|
||||
4. **共识算法**:Raft 是目前最实用的共识算法,etcd/Consul 都基于它
|
||||
5. **分布式事务**:Saga 适合大多数场景,TCC 适合金融场景,2PC 适合数据库层
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [Designing Data-Intensive Applications](https://dataintensive.net/) - Martin Kleppmann 的分布式系统经典
|
||||
- [The Raft Consensus Algorithm](https://raft.github.io/) - Raft 官方可视化演示
|
||||
- [CAP Twelve Years Later](https://www.infoq.com/articles/cap-twelve-years-later-how-the-rules-have-changed/) - Brewer 对 CAP 的重新审视
|
||||
- [Jepsen](https://jepsen.io/) - 分布式系统正确性测试框架
|
||||
- [分布式系统模式](https://martinfowler.com/articles/patterns-of-distributed-systems/) - Martin Fowler 的分布式模式合集
|
||||
@@ -1,3 +1,204 @@
|
||||
# 高可用与容灾
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**系统挂了 1 分钟,可能意味着几十万的损失。** 高可用(High Availability)是指系统在面对硬件故障、软件 Bug、网络问题等异常情况时,仍能持续提供服务的能力。容灾(Disaster Recovery)则是在更大范围的灾难发生时,系统能够恢复服务的能力。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **可用性度量**:理解"几个 9"的含义和对应的停机时间
|
||||
- **故障转移**:掌握主备、主主、多活等高可用架构
|
||||
- **容灾策略**:了解 RPO 和 RTO 的概念及设计方法
|
||||
- **故障检测**:理解心跳、探针、熔断等故障发现机制
|
||||
- **混沌工程**:了解如何主动注入故障来验证系统韧性
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 可用性度量 | SLA、几个 9、停机时间 |
|
||||
| **第 2 章** | 故障转移架构 | 主备、主主、多可用区、异地多活 |
|
||||
| **第 3 章** | 容灾设计 | RPO、RTO、备份策略 |
|
||||
| **第 4 章** | 故障检测与恢复 | 心跳、熔断、自动扩缩容 |
|
||||
| **第 5 章** | 混沌工程 | 故障注入、韧性验证 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 可用性度量:几个 9 意味着什么?
|
||||
|
||||
可用性通常用"几个 9"来衡量,计算公式为:
|
||||
|
||||
**可用性 = 正常运行时间 / 总时间 × 100%**
|
||||
|
||||
例如一个月(30 天 = 43200 分钟)内停机了 43 分钟,可用性就是 (43200 - 43) / 43200 ≈ 99.9%。每多一个 9,允许的停机时间就少一个数量级,实现难度和成本也指数级增长。
|
||||
|
||||
| 可用性等级 | 百分比 | 每月允许停机 | 每年允许停机 | 典型要求 |
|
||||
|-----------|--------|------------|------------|---------|
|
||||
| 2 个 9 | 99% | 7.3 小时 | 3.65 天 | 内部工具 |
|
||||
| 3 个 9 | 99.9% | 43 分钟 | 8.76 小时 | 普通业务系统 |
|
||||
| 4 个 9 | 99.99% | 4.3 分钟 | 52.6 分钟 | 电商、SaaS |
|
||||
| 5 个 9 | 99.999% | 26 秒 | 5.26 分钟 | 金融、支付 |
|
||||
|
||||
<AvailabilityCalculatorDemo />
|
||||
|
||||
::: tip SLA 是什么?
|
||||
**SLA(Service Level Agreement,服务等级协议)** 是服务提供方与客户之间的正式承诺。比如 AWS S3 承诺 99.99% 的可用性,如果没达到,会按比例退款。SLA 不只是技术指标,更是商业合同——违反 SLA 意味着赔钱。
|
||||
:::
|
||||
|
||||
::: tip 从 3 个 9 到 4 个 9 的鸿沟
|
||||
3 个 9(99.9%)意味着每月可以停机 43 分钟——一次部署出问题,回滚一下就用完了。
|
||||
4 个 9(99.99%)意味着每月只能停机 4 分钟——这要求你必须有自动故障转移、滚动部署、健康检查等完整的高可用体系。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 故障转移架构
|
||||
|
||||
故障转移(Failover)是高可用的核心机制:当主节点故障时,自动切换到备用节点继续提供服务。
|
||||
|
||||
### 主备模式(Active-Standby)
|
||||
|
||||
最常见的高可用架构。主节点处理所有请求,备节点实时同步数据但不处理请求。主节点故障时,备节点自动接管。
|
||||
|
||||
```
|
||||
正常状态:
|
||||
客户端 → 主节点(处理请求)
|
||||
备节点(同步数据,待命)
|
||||
|
||||
故障转移:
|
||||
客户端 → 备节点(接管为新主节点)
|
||||
原主节点(故障,等待修复)
|
||||
```
|
||||
|
||||
关键问题是**脑裂(Split Brain)**:网络分区时,主备节点都认为对方挂了,同时对外提供服务,导致数据不一致。解决方案是引入**仲裁节点(Quorum)**——至少 3 个节点投票决定谁是主节点。
|
||||
|
||||
### 多可用区(Multi-AZ)
|
||||
|
||||
将服务部署在同一地域的多个数据中心(可用区)。单个数据中心断电、断网不影响整体服务。云厂商的可用区之间通常有低延迟专线连接(< 2ms)。
|
||||
|
||||
### 异地多活(Multi-Region Active-Active)
|
||||
|
||||
在不同城市甚至不同国家部署完整的服务副本,每个站点都能独立处理请求。这是最高级别的高可用架构,但也最复杂——核心挑战是**跨地域数据同步**的延迟和一致性问题。
|
||||
|
||||
<FailoverStrategyDemo />
|
||||
|
||||
| 架构 | 可用性级别 | 成本 | 复杂度 | 适用场景 |
|
||||
|------|-----------|------|--------|---------|
|
||||
| 单机 | 99%~99.9% | 低 | 低 | 开发测试、内部工具 |
|
||||
| 主备 | 99.9%~99.99% | 中 | 中 | 中小型业务系统 |
|
||||
| 多可用区 | 99.99% | 高 | 高 | 电商、SaaS 平台 |
|
||||
| 异地多活 | 99.999% | 极高 | 极高 | 金融、大型互联网 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 容灾设计:RPO 与 RTO
|
||||
|
||||
容灾设计围绕两个核心指标展开:
|
||||
|
||||
| 指标 | 全称 | 含义 | 举例 |
|
||||
|------|------|------|------|
|
||||
| RPO | Recovery Point Objective | 能容忍丢失多少数据 | RPO=0 表示不能丢任何数据 |
|
||||
| RTO | Recovery Time Objective | 能容忍停机多长时间 | RTO=5min 表示 5 分钟内恢复 |
|
||||
|
||||
### 备份策略与 RPO 的关系
|
||||
|
||||
| 备份方式 | RPO | 成本 | 说明 |
|
||||
|---------|-----|------|------|
|
||||
| 每日全量备份 | 24 小时 | 低 | 最多丢一天数据 |
|
||||
| 实时增量备份 | 分钟级 | 中 | binlog/WAL 持续同步 |
|
||||
| 同步复制 | 0 | 高 | 写入必须等副本确认 |
|
||||
|
||||
::: tip 不是所有数据都需要 RPO=0
|
||||
用户头像丢了可以重新上传(RPO=24h 够了),但支付记录一条都不能丢(RPO=0)。根据数据的业务价值来决定备份策略,而不是一刀切。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 4. 故障检测与恢复
|
||||
|
||||
### 4.1 故障检测机制
|
||||
|
||||
| 机制 | 原理 | 检测速度 | 适用场景 |
|
||||
|------|------|---------|---------|
|
||||
| 心跳检测 | 定期发送心跳包,超时判定故障 | 秒级 | 节点存活检测 |
|
||||
| 健康检查 | HTTP/TCP 探针检查服务状态 | 秒级 | 负载均衡器后端检测 |
|
||||
| 业务探针 | 模拟真实请求检查业务逻辑 | 秒~分钟级 | 端到端可用性监控 |
|
||||
|
||||
**心跳检测的工作原理**:节点 A 每隔固定时间(如 5 秒)向监控方发送一个"我还活着"的信号。如果连续 N 次(如 3 次)没收到心跳,就判定节点 A 故障。关键参数是**心跳间隔**和**超时阈值**——间隔太短会增加网络开销,太长会延迟故障发现。
|
||||
|
||||
**健康检查的三种级别**:
|
||||
- **存活探针(Liveness)**:进程还在运行吗?不在就重启
|
||||
- **就绪探针(Readiness)**:服务能接受请求吗?不能就从负载均衡中摘除
|
||||
- **启动探针(Startup)**:服务启动完成了吗?没完成就等待,不要误判为故障
|
||||
|
||||
### 4.2 自动恢复机制
|
||||
|
||||
| 机制 | 描述 | 典型工具 |
|
||||
|------|------|---------|
|
||||
| 自动重启 | 进程崩溃后自动拉起 | systemd、PM2、K8s |
|
||||
| 自动扩缩容 | 负载升高时自动增加实例 | K8s HPA、云厂商 Auto Scaling |
|
||||
| 熔断降级 | 下游故障时快速失败,防止级联故障 | Hystrix、Sentinel、Resilience4j |
|
||||
| 限流 | 超过容量的请求直接拒绝 | Nginx limit_req、网关限流 |
|
||||
|
||||
**熔断器模式(Circuit Breaker)详解**:
|
||||
|
||||
熔断器的灵感来自电路中的保险丝——当电流过大时自动断开,保护整个电路不被烧毁。在微服务中,当下游服务故障时,熔断器会"断开",让请求快速失败,而不是傻等超时。
|
||||
|
||||
```
|
||||
熔断器三种状态:
|
||||
|
||||
关闭(正常)──→ 失败率超过阈值 ──→ 打开(熔断)
|
||||
↑ │
|
||||
│ 等待冷却时间
|
||||
│ ↓
|
||||
└── 探测请求成功 ←── 半开(试探)
|
||||
```
|
||||
|
||||
- **关闭状态**:正常转发请求,同时统计失败率
|
||||
- **打开状态**:所有请求直接返回错误(快速失败),不再调用下游
|
||||
- **半开状态**:冷却时间到后,放行少量探测请求。如果成功,恢复关闭;如果失败,继续打开
|
||||
|
||||
**降级(Fallback)** 是熔断的配套策略:熔断触发后,不是直接报错,而是返回一个"兜底"结果。比如推荐服务挂了,就返回热门商品列表;用户头像加载失败,就显示默认头像。
|
||||
|
||||
---
|
||||
|
||||
## 5. 混沌工程:主动找问题
|
||||
|
||||
混沌工程的核心理念是:**与其等故障发生,不如主动制造故障**,在可控环境中验证系统的韧性。
|
||||
|
||||
| 工具 | 提出者 | 核心能力 |
|
||||
|------|--------|---------|
|
||||
| Chaos Monkey | Netflix | 随机终止生产环境的实例 |
|
||||
| Chaos Mesh | PingCAP | K8s 环境下的故障注入 |
|
||||
| Litmus | CNCF | 云原生混沌工程框架 |
|
||||
| ChaosBlade | 阿里巴巴 | 多场景故障注入工具 |
|
||||
|
||||
::: tip 混沌工程的实施步骤
|
||||
1. **定义稳态**:明确系统正常运行的指标(如 P99 延迟 < 200ms)
|
||||
2. **提出假设**:如果某个节点挂了,系统应该在 30 秒内自动恢复
|
||||
3. **注入故障**:在可控范围内制造故障(先在测试环境,再到生产)
|
||||
4. **观察结果**:系统是否如预期恢复?有没有级联故障?
|
||||
5. **修复弱点**:发现问题后改进架构和流程
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
高可用不是一个功能,而是一种架构能力。它需要从设计、开发、部署、运维的每个环节去保障。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **几个 9**:每多一个 9,停机时间少一个数量级,成本和复杂度指数增长
|
||||
2. **故障转移**:从主备到异地多活,根据业务需求选择合适的架构
|
||||
3. **RPO 与 RTO**:根据数据价值和业务容忍度设计备份和恢复策略
|
||||
4. **自动化**:故障检测、自动重启、熔断降级是高可用的基础设施
|
||||
5. **混沌工程**:主动制造故障,在可控环境中验证系统韧性
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [Site Reliability Engineering](https://sre.google/sre-book/table-of-contents/) - Google SRE 经典
|
||||
- [Chaos Monkey](https://netflix.github.io/chaosmonkey/) - Netflix 混沌工程工具
|
||||
- [Release It!](https://pragprog.com/titles/mnee2/release-it-second-edition/) - 生产环境设计模式
|
||||
- [Chaos Mesh](https://chaos-mesh.org/) - K8s 混沌工程平台
|
||||
|
||||
|
||||
@@ -1,3 +1,156 @@
|
||||
# 从单体到微服务的演进
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**没有哪个架构是"最好的",只有"最适合当前阶段的"。** 从单体到微服务不是一步到位的跳跃,而是随着业务规模和团队规模增长,逐步演进的过程。过早拆分微服务和过晚拆分一样危险。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **演进路径**:理解从单体到微服务的四个阶段
|
||||
- **拆分时机**:知道什么时候该拆、什么时候不该拆
|
||||
- **拆分策略**:掌握按业务域拆分的方法论
|
||||
- **通信模式**:了解服务间同步和异步通信的选择
|
||||
- **数据拆分**:理解数据库拆分的挑战和方案
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 架构演进路径 | 单体→模块化→SOA→微服务 |
|
||||
| **第 2 章** | 拆分时机与原则 | Conway 定律、团队自治 |
|
||||
| **第 3 章** | 拆分策略 | DDD 限界上下文、绞杀者模式 |
|
||||
| **第 4 章** | 服务通信 | REST、gRPC、消息队列 |
|
||||
| **第 5 章** | 数据拆分 | 数据库拆分、数据同步 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 架构演进路径
|
||||
|
||||
架构演进不是技术驱动的,而是**组织规模驱动的**。当团队从 5 人增长到 500 人时,单体架构的协作效率会急剧下降。
|
||||
|
||||
| 阶段 | 架构 | 团队规模 | 特点 |
|
||||
|------|------|---------|------|
|
||||
| 起步期 | 单体应用 | 1~10 人 | 所有代码在一个项目中,部署简单 |
|
||||
| 成长期 | 模块化单体 | 10~50 人 | 代码按模块划分,但仍然一起部署 |
|
||||
| 扩张期 | SOA(面向服务) | 50~200 人 | 按业务线拆分为粗粒度服务 |
|
||||
| 规模期 | 微服务 | 200+ 人 | 细粒度服务,每个团队独立开发部署 |
|
||||
|
||||
<ArchEvolutionDemo />
|
||||
|
||||
::: tip Conway 定律
|
||||
"设计系统的组织,其产生的架构等同于组织的沟通结构。"——Melvin Conway
|
||||
|
||||
简单说:3 个团队做一个系统,最终会变成 3 个服务。架构拆分的本质是**组织拆分**。
|
||||
|
||||
**反向 Conway 定律**:既然组织结构决定了系统架构,那么想要什么样的架构,就先调整成什么样的组织结构。比如你想拆出独立的支付服务,就先组建一个独立的支付团队。很多公司微服务拆分失败,不是技术问题,而是组织没有跟着调整。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 什么时候该拆微服务?
|
||||
|
||||
不是所有系统都需要微服务。过早拆分会带来不必要的复杂性。
|
||||
|
||||
| 信号 | 说明 | 建议 |
|
||||
|------|------|------|
|
||||
| 部署冲突频繁 | 多个团队改同一个代码库,经常冲突 | 考虑拆分 |
|
||||
| 某模块需要独立扩容 | 搜索模块需要 10 倍于其他模块的资源 | 考虑拆分 |
|
||||
| 技术栈需要差异化 | AI 模块用 Python,主站用 Java | 考虑拆分 |
|
||||
| 团队 < 10 人 | 沟通成本低,单体足够 | 不要拆 |
|
||||
| 业务还在探索期 | 需求变化快,边界不清晰 | 不要拆 |
|
||||
| 没有 DevOps 能力 | 没有 CI/CD、容器化、监控体系 | 不要拆 |
|
||||
|
||||
---
|
||||
|
||||
## 3. 拆分策略
|
||||
|
||||
### 3.1 按业务域拆分(DDD 限界上下文)
|
||||
|
||||
DDD(领域驱动设计)的限界上下文(Bounded Context)是拆分微服务的最佳指导原则。每个限界上下文对应一个独立的业务域,有自己的数据模型和业务规则。
|
||||
|
||||
**什么是限界上下文?** 同一个词在不同业务域中含义不同。比如"用户"在用户域是指注册信息(姓名、邮箱),在订单域是指下单人(收货地址、支付方式),在推荐域是指行为画像(浏览历史、偏好标签)。限界上下文就是划定一个边界,在这个边界内,术语和模型有明确统一的含义。
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
|
||||
│ 用户域 │ │ 订单域 │ │ 支付域 │
|
||||
│ │ │ │ │ │
|
||||
│ User │ │ Order │ │ Payment │
|
||||
│ Profile │ │ OrderItem │ │ Refund │
|
||||
│ Address │ │ Cart │ │ Transaction │
|
||||
│ │ │ │ │ │
|
||||
│ 用户服务 │ │ 订单服务 │ │ 支付服务 │
|
||||
└──────┬──────┘ └──────┬──────┘ └──────┬──────┘
|
||||
│ │ │
|
||||
└────── API 调用 / 事件通信 ───────┘
|
||||
```
|
||||
|
||||
| 限界上下文 | 核心实体 | 对应服务 |
|
||||
|-----------|---------|---------|
|
||||
| 用户域 | User、Profile、Address | 用户服务 |
|
||||
| 商品域 | Product、Category、SKU | 商品服务 |
|
||||
| 订单域 | Order、OrderItem | 订单服务 |
|
||||
| 支付域 | Payment、Refund | 支付服务 |
|
||||
| 物流域 | Shipment、Tracking | 物流服务 |
|
||||
|
||||
### 3.2 绞杀者模式(Strangler Fig Pattern)
|
||||
|
||||
不要一次性重写整个单体,而是像绞杀榕一样,逐步用新服务替换旧模块:
|
||||
|
||||
1. 在单体外部创建新服务
|
||||
2. 通过代理层将部分流量路由到新服务
|
||||
3. 验证新服务稳定后,逐步迁移更多流量
|
||||
4. 最终完全替换旧模块
|
||||
|
||||
---
|
||||
|
||||
## 4. 服务通信模式
|
||||
|
||||
| 方式 | 协议 | 特点 | 适用场景 |
|
||||
|------|------|------|---------|
|
||||
| REST | HTTP/JSON | 简单通用,生态好 | 对外 API、CRUD 操作 |
|
||||
| gRPC | HTTP/2 + Protobuf | 高性能,强类型 | 内部服务间高频调用 |
|
||||
| 消息队列 | AMQP/Kafka | 异步解耦,削峰填谷 | 事件通知、异步任务 |
|
||||
| GraphQL | HTTP/JSON | 客户端按需查询 | BFF 层、移动端 |
|
||||
|
||||
::: tip 同步 vs 异步的选择
|
||||
- **需要立即返回结果** → 同步(REST/gRPC)
|
||||
- **不需要立即返回** → 异步(消息队列)
|
||||
- **一个事件触发多个动作** → 异步(发布-订阅)
|
||||
|
||||
经验法则:能异步就异步,同步调用链越长,系统越脆弱。
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 5. 数据拆分:最难的部分
|
||||
|
||||
微服务拆分中最痛苦的不是代码拆分,而是数据库拆分。每个服务应该拥有自己的数据库,但这意味着跨服务查询变得困难。
|
||||
|
||||
| 挑战 | 描述 | 解决方案 |
|
||||
|------|------|---------|
|
||||
| 跨服务 JOIN | 不能直接 JOIN 两个服务的表 | API 组合查询、数据冗余 |
|
||||
| 分布式事务 | 跨库事务无法用本地事务 | Saga、本地消息表 |
|
||||
| 数据一致性 | 多个服务的数据可能暂时不一致 | 最终一致性、事件驱动 |
|
||||
| 数据迁移 | 从共享库迁移到独立库 | 双写过渡、数据同步工具 |
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
从单体到微服务是一个渐进的过程,不是一蹴而就的革命。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **演进路径**:单体→模块化单体→SOA→微服务,每一步都有明确的驱动力
|
||||
2. **拆分时机**:团队规模、部署冲突、扩容需求是拆分的信号
|
||||
3. **拆分策略**:用 DDD 限界上下文指导拆分,用绞杀者模式渐进迁移
|
||||
4. **通信选择**:能异步就异步,同步调用链越短越好
|
||||
5. **数据拆分**:最难但最重要,接受最终一致性是关键心态转变
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [Building Microservices](https://www.oreilly.com/library/view/building-microservices-2nd/9781492034018/) - Sam Newman 微服务经典
|
||||
- [Monolith to Microservices](https://www.oreilly.com/library/view/monolith-to-microservices/9781492047834/) - 渐进式迁移指南
|
||||
- [Domain-Driven Design](https://www.domainlanguage.com/ddd/) - Eric Evans 的 DDD 经典
|
||||
- [The Strangler Fig Pattern](https://martinfowler.com/bliki/StranglerFigApplication.html) - Martin Fowler 的绞杀者模式
|
||||
|
||||
|
||||
@@ -1,3 +1,252 @@
|
||||
# 系统设计方法论
|
||||
|
||||
> 待实现
|
||||
::: tip 前言
|
||||
**系统设计不是拍脑袋画架构图,而是一套有章可循的方法论。** 无论是面试中的系统设计题,还是实际工作中的架构设计,都遵循相似的思考框架:先搞清楚问题,再估算规模,然后设计方案,最后深入优化。
|
||||
:::
|
||||
|
||||
**这篇文章会带你学什么?**
|
||||
|
||||
学完这章后,你将获得:
|
||||
|
||||
- **设计流程**:掌握系统设计的四步法框架
|
||||
- **容量估算**:学会"信封背面估算"的技巧
|
||||
- **常见模式**:熟悉缓存、分库分表、消息队列等核心模式
|
||||
- **权衡思维**:理解架构设计中的 trade-off 思维
|
||||
- **实战案例**:通过短链服务、Feed 流等案例理解设计过程
|
||||
|
||||
| 章节 | 内容 | 核心概念 |
|
||||
|-----|------|---------|
|
||||
| **第 1 章** | 设计四步法 | 需求澄清、容量估算、架构设计、深入优化 |
|
||||
| **第 2 章** | 容量估算 | QPS、存储、带宽、信封背面估算 |
|
||||
| **第 3 章** | 核心设计模式 | 缓存、分库分表、消息队列、CDN |
|
||||
| **第 4 章** | 权衡思维 | 一致性 vs 可用性、性能 vs 成本 |
|
||||
| **第 5 章** | 经典案例 | 短链服务、Feed 流、秒杀系统 |
|
||||
|
||||
---
|
||||
|
||||
## 1. 系统设计四步法
|
||||
|
||||
系统设计不是一上来就画架构图。无论是面试还是实战,都应该遵循一个结构化的流程。
|
||||
|
||||
<SystemDesignStepsDemo />
|
||||
|
||||
::: tip 为什么要先澄清需求?
|
||||
很多人拿到题目就开始画图,结果设计了一个"正确但不是面试官想要的"系统。花 5 分钟问清楚需求,能避免后面 30 分钟的返工。
|
||||
|
||||
常见的澄清问题:
|
||||
- 系统的核心功能是什么?(不要设计所有功能)
|
||||
- 用户规模多大?(决定是否需要分布式)
|
||||
- 读写比例?(决定缓存策略)
|
||||
- 数据需要保留多久?(决定存储方案)
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 2. 容量估算:信封背面的艺术
|
||||
|
||||
"信封背面估算"(Back-of-envelope estimation)是系统设计中的核心技能。不需要精确计算,只需要知道量级。
|
||||
|
||||
<CapacityEstimationDemo />
|
||||
|
||||
### 常用换算速查
|
||||
|
||||
| 量级 | 换算 | 记忆技巧 |
|
||||
|------|------|---------|
|
||||
| 1 天 | 86,400 秒 | ≈ 10 万秒 |
|
||||
| 1 亿请求/天 | ≈ 1,200 QPS | 除以 10 万 |
|
||||
| 1 KB × 1 亿 | ≈ 100 GB | 1 亿条小记录 |
|
||||
| 1 MB × 100 万 | ≈ 1 TB | 100 万张图片 |
|
||||
|
||||
### 2-8 法则在估算中的应用
|
||||
|
||||
大多数系统遵循 80/20 法则:20% 的数据承载 80% 的请求。这意味着:
|
||||
|
||||
- **缓存大小** ≈ 总数据量 × 20%
|
||||
- **热点 QPS** ≈ 总 QPS × 80% 集中在 20% 的 key 上
|
||||
- **缓存命中率**目标 ≈ 80%+(低于这个值说明缓存策略有问题)
|
||||
|
||||
---
|
||||
|
||||
## 3. 核心设计模式
|
||||
|
||||
系统设计中反复出现的模式,掌握这些就能应对大多数场景。
|
||||
|
||||
### 3.1 缓存模式
|
||||
|
||||
| 模式 | 读路径 | 写路径 | 适用场景 |
|
||||
|------|--------|--------|---------|
|
||||
| Cache-Aside | 先查缓存,miss 则查 DB 并回填 | 先写 DB,再删缓存 | 通用场景,最常用 |
|
||||
| Read-Through | 缓存层自动从 DB 加载 | 同 Cache-Aside | 需要缓存框架支持 |
|
||||
| Write-Behind | 同 Cache-Aside | 先写缓存,异步写 DB | 写密集型,可容忍丢数据 |
|
||||
|
||||
::: tip 为什么是"删缓存"而不是"更新缓存"?
|
||||
更新缓存在并发场景下容易出现数据不一致:线程 A 和 B 同时更新,A 先写 DB 但 B 先更新缓存,导致缓存中是 B 的旧值。删除缓存则让下次读请求重新从 DB 加载,天然避免这个问题。
|
||||
:::
|
||||
|
||||
### 3.2 分库分表
|
||||
|
||||
当单表数据量超过千万级,或单库 QPS 超过瓶颈时,就需要考虑分库分表。
|
||||
|
||||
| 策略 | 做法 | 优点 | 缺点 |
|
||||
|------|------|------|------|
|
||||
| 垂直分库 | 按业务域拆分数据库 | 业务解耦,独立扩展 | 跨库 JOIN 困难 |
|
||||
| 水平分表 | 同一张表按规则拆成多张 | 单表数据量可控 | 分片键选择关键 |
|
||||
| 垂直分表 | 把大字段拆到独立表 | 减少 IO,提升查询效率 | 需要额外 JOIN |
|
||||
|
||||
**分片键选择原则**:
|
||||
- 选择查询最频繁的字段(如 user_id)
|
||||
- 数据分布要均匀,避免热点
|
||||
- 尽量让同一用户的数据在同一分片(减少跨分片查询)
|
||||
|
||||
### 3.3 消息队列
|
||||
|
||||
消息队列是分布式系统的"减震器",核心作用是解耦、异步、削峰。
|
||||
|
||||
| 场景 | 不用队列 | 用队列 |
|
||||
|------|---------|--------|
|
||||
| 下单后发通知 | 下单接口同步调用通知服务,通知失败导致下单失败 | 下单成功后发消息,通知服务异步消费 |
|
||||
| 秒杀抢购 | 瞬间流量打爆数据库 | 请求先入队列,后端按能力消费 |
|
||||
| 数据同步 | 服务 A 直接调用服务 B 的接口 | 服务 A 发事件,服务 B 订阅处理 |
|
||||
|
||||
---
|
||||
|
||||
## 4. 权衡思维:没有银弹
|
||||
|
||||
架构设计的本质是权衡(Trade-off)。每个决策都有代价,关键是理解代价并做出适合当前阶段的选择。
|
||||
|
||||
| 权衡维度 | 选项 A | 选项 B | 决策依据 |
|
||||
|---------|--------|--------|---------|
|
||||
| 一致性 vs 可用性 | 强一致(CP) | 高可用(AP) | 业务能否容忍短暂不一致? |
|
||||
| 性能 vs 成本 | 全量缓存 | 按需缓存 | 数据量和预算 |
|
||||
| 简单 vs 灵活 | 单体架构 | 微服务 | 团队规模和业务复杂度 |
|
||||
| 实时 vs 批量 | 流式处理 | 批处理 | 数据时效性要求 |
|
||||
| 自建 vs 托管 | 自己搭 MySQL | 用云数据库 RDS | 运维能力和成本 |
|
||||
|
||||
::: tip 架构决策记录(ADR)
|
||||
每个重要的架构决策都应该记录下来:**背景是什么、考虑了哪些方案、为什么选了这个、有什么代价**。这不是为了甩锅,而是为了让后来的人理解"为什么当时这么设计"。
|
||||
|
||||
格式很简单:
|
||||
- **标题**:用 XXX 替代 YYY
|
||||
- **背景**:我们遇到了什么问题
|
||||
- **决策**:我们选择了什么方案
|
||||
- **理由**:为什么选这个
|
||||
- **代价**:这个决策的缺点和风险
|
||||
:::
|
||||
|
||||
### 常见的错误权衡
|
||||
|
||||
| 错误 | 表现 | 正确做法 |
|
||||
|------|------|---------|
|
||||
| 过早优化 | 日活 1000 就上分库分表 | 先用单库,遇到瓶颈再拆 |
|
||||
| 技术驱动 | "我想用 Kafka" 而不是 "我需要异步" | 从问题出发,而非从技术出发 |
|
||||
| 忽略运维成本 | 选了最优方案但团队维护不了 | 方案要匹配团队能力 |
|
||||
| 追求完美一致性 | 所有场景都用分布式事务 | 大多数场景最终一致性就够了 |
|
||||
|
||||
---
|
||||
|
||||
## 5. 经典案例
|
||||
|
||||
通过三个经典案例,把前面学到的方法论串起来。
|
||||
|
||||
### 5.1 短链服务(TinyURL)
|
||||
|
||||
短链服务是系统设计面试的经典题目,麻雀虽小五脏俱全。
|
||||
|
||||
**需求澄清**:
|
||||
- 核心功能:长链接 → 短链接(写),短链接 → 重定向(读)
|
||||
- 读写比:约 100:1(读远多于写)
|
||||
- 日均重定向:1 亿次
|
||||
- 短链永不过期
|
||||
|
||||
**容量估算**:
|
||||
|
||||
| 指标 | 计算 | 结果 |
|
||||
|------|------|------|
|
||||
| 写 QPS | 1 亿 / 100 / 86400 | ≈ 12 QPS |
|
||||
| 读 QPS | 1 亿 / 86400 | ≈ 1,200 QPS |
|
||||
| 峰值读 QPS | 1,200 × 3 | ≈ 3,600 QPS |
|
||||
| 5 年存储 | 100 万/天 × 365 × 5 × 100B | ≈ 18 GB |
|
||||
| 缓存(20%) | 18 GB × 20% | ≈ 3.6 GB |
|
||||
|
||||
**架构设计**:
|
||||
|
||||
```
|
||||
写路径:客户端 → API Server → ID 生成器 → Base62 编码 → 写入 MySQL + Redis
|
||||
读路径:客户端 → CDN → API Server → Redis 查询 → 302 重定向
|
||||
↓ (cache miss)
|
||||
MySQL 查询 → 回填 Redis
|
||||
```
|
||||
|
||||
**关键设计决策**:
|
||||
- 短码生成:Snowflake 分布式 ID + Base62 编码,避免哈希冲突
|
||||
- 缓存策略:Cache-Aside,热点短链用 CDN 加速
|
||||
- 数据库:单表即可(18GB 很小),按短码做索引
|
||||
|
||||
### 5.2 Feed 流系统
|
||||
|
||||
社交平台的 Feed 流(朋友圈、微博首页)是另一个经典题目。
|
||||
|
||||
**核心挑战**:用户发一条动态,如何让所有关注者看到?
|
||||
|
||||
| 方案 | 做法 | 优点 | 缺点 |
|
||||
|------|------|------|------|
|
||||
| 拉模式(Pull) | 读取时实时聚合关注者的动态 | 写入简单,存储少 | 读取慢,关注多时延迟高 |
|
||||
| 推模式(Push) | 发布时写入所有粉丝的收件箱 | 读取极快 | 大 V 发动态写扩散严重 |
|
||||
| 推拉结合 | 普通用户推,大 V 拉 | 平衡读写性能 | 实现复杂 |
|
||||
|
||||
**推拉结合方案**:
|
||||
- 粉丝数 < 1 万:发布时推送到所有粉丝的 Feed 缓存(推模式)
|
||||
- 粉丝数 > 1 万:不推送,粉丝读取时实时拉取(拉模式)
|
||||
- 用户打开 Feed 时:合并推送的内容 + 实时拉取大 V 的内容,按时间排序
|
||||
|
||||
### 5.3 秒杀系统
|
||||
|
||||
秒杀的核心挑战:瞬间超高并发 + 库存不能超卖。
|
||||
|
||||
**流量特征**:
|
||||
- 活动开始前:大量用户刷新页面等待
|
||||
- 活动开始瞬间:QPS 可能是平时的 100 倍以上
|
||||
- 活动结束后:流量迅速回落
|
||||
|
||||
**分层削峰策略**:
|
||||
|
||||
```
|
||||
用户请求 → CDN(静态页面)→ 网关(限流)→ 消息队列(削峰)→ 库存服务(扣减)
|
||||
```
|
||||
|
||||
| 层级 | 策略 | 效果 |
|
||||
|------|------|------|
|
||||
| 前端 | 按钮置灰 + 随机延迟 + 验证码 | 过滤机器人,分散请求 |
|
||||
| CDN | 静态资源缓存 | 减少 90% 的页面请求 |
|
||||
| 网关 | 令牌桶限流 | 只放行系统能承受的流量 |
|
||||
| 消息队列 | 请求入队,异步处理 | 削峰填谷,保护数据库 |
|
||||
| 库存服务 | Redis 预扣减 + Lua 原子操作 | 防止超卖,毫秒级响应 |
|
||||
|
||||
::: tip 秒杀的核心原则
|
||||
1. **尽量拦截在上游**:能在 CDN 挡住的就不要到应用层
|
||||
2. **读写分离**:商品详情页走缓存,只有下单走数据库
|
||||
3. **异步处理**:用户点击"抢购"后立即返回"排队中",后台异步处理
|
||||
4. **兜底方案**:限流、熔断、降级,任何一层出问题都有 Plan B
|
||||
:::
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
系统设计是一门实践性很强的技能,核心在于结构化思考和权衡取舍。
|
||||
|
||||
回顾本章的关键要点:
|
||||
|
||||
1. **四步法框架**:需求澄清 → 容量估算 → 架构设计 → 深入优化,每一步都不可跳过
|
||||
2. **信封背面估算**:不需要精确,只需要知道量级,用于指导架构决策
|
||||
3. **核心模式**:缓存、分库分表、消息队列、CDN、限流熔断——这些是系统设计的"积木"
|
||||
4. **权衡思维**:没有完美方案,只有适合当前阶段的方案,记录每个决策的理由和代价
|
||||
5. **经典案例**:短链服务练基础、Feed 流练推拉模型、秒杀练高并发——掌握这三个就能举一反三
|
||||
|
||||
## 延伸阅读
|
||||
|
||||
- [System Design Interview](https://www.amazon.com/System-Design-Interview-insiders-Second/dp/B08CMF2CQF) - Alex Xu 系统设计面试经典
|
||||
- [Designing Data-Intensive Applications](https://dataintensive.net/) - Martin Kleppmann 数据密集型应用设计
|
||||
- [The System Design Primer](https://github.com/donnemartin/system-design-primer) - GitHub 上最全的系统设计学习资源
|
||||
- [ByteByteGo](https://bytebytego.com/) - Alex Xu 的系统设计可视化博客
|
||||
|
||||
|
||||
Reference in New Issue
Block a user