分布式系统的书籍很多,但 Brendan Burns 这本书有一个独特的优势:作者是 Kubernetes 的三位创始者之一(另外两位是 Joe Beda 和 Craig McLuckie)。这本书不是纯理论,而是来自 Google 大规模运维经验的实际总结。Burns 将分布式系统中的反复出现的问题抽象为可复用的模式(Patterns),让读者能够像学习设计模式一样,系统地掌握分布式系统的设计方法。
核心价值:模式化思维——把看似复杂的分布式系统问题,拆解成一个个可复用的模式,然后组合使用。
与传统分布式系统书籍(如《数据密集型应用系统设计》DDIA)相比,本书的定位是"实战入门向导"而非"理论参考书"。DDIA 每章可达 80-100 页深入单一主题,而本书全书仅 ~200 页,覆盖从单节点到多节点再到批处理的 3 个层次 10+ 个模式。这种设计让读者能在 2-3 周内建立分布式系统的全景认知,而非花 2-3 个月才深入第一个主题。
| 维度 | 本书 | DDIA | 微服务架构设计模式 |
|---|---|---|---|
| 页数 | ~200 页 | ~600 页 | ~450 页 |
| 代码示例 | 20+ 个 Python 伪代码 | 少量 | 大量 Java 代码 |
| 理论深度 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐⭐ |
| 实战导向 | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 阅读周期(推荐) | 2-3 周 | 2-3 月 | 1-2 月 |
| 前置要求 | 基础 K8s 概念 | 数据结构/算法 | Java/微服务经验 |
整本书围绕三个层次展开:
┌─────────────────────────────────────────┐
│ 第三层:有状态与批处理模式 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ 缓存 │ │ 复制/共识│ │批处理 │ │
│ │ │ │ │ │MapReduce│ │
│ └─────────┘ └─────────┘ └─────────┘ │
├─────────────────────────────────────────┤
│ 第二层:多节点模式 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │Leader │ │Work │ │Scatter │ │
│ │Election │ │Queue │ │/Gather │ │
│ └─────────┘ └─────────┘ └─────────┘ │
├─────────────────────────────────────────┤
│ 第一层:单节点模式 │
│ ┌─────────┐ ┌─────────┐ ┌─────────┐ │
│ │ Sidecar │ │Ambassador│ │ Adapter │ │
│ │(辅助) │ │(对外通信)│ │(标准化) │ │
│ └─────────┘ └─────────┘ └─────────┘ │
└─────────────────────────────────────────┘
这一层次的模式针对单个容器/Pod 内的多个容器协作,解决"一个 Pod 里放多个容器时,它们如何分工配合"的问题。
Sidecar 模式并非零成本。以 Istio 的 Envoy Sidecar 为例,实测性能影响如下:
| 指标 | 无 Sidecar | Envoy Sidecar | 性能影响 |
|---|---|---|---|
| 请求延迟 P50 | 1.0 ms | 1.2-1.5 ms | +20-50% |
| 请求延迟 P99 | 5.0 ms | 8.0-12.0 ms | +60-140% |
| CPU 额外占用 | - | 每 Pod 30-80 mCPU | ~5% 节点开销 |
| 内存额外占用 | - | 每 Pod 40-128 MB | 视连接数而定 |
| 请求吞吐量 | 10,000 req/s | 7,500-9,000 req/s | -10-25% |
| TLS 握手延迟 | - | +0.5-2.0 ms | 首次连接 |
更精确的延迟分解:Envoy Sidecar 引入的延迟主要由以下部分组成:
其中:
优化建议:如果应用对延迟极端敏感(如高频交易场景,P99 < 5ms),考虑使用 eBPF 替代 iptables 的 Cilium 方案,可降低 Sidecar 网络延迟 40-60%。
以典型的 EFK(Elasticsearch + Fluentd/Fluent Bit + Kibana)日志收集为例:
主容器 (Nginx)
├── 写入 /var/log/nginx/access.log ← 共享 Volume
└── 写入 /var/log/nginx/error.log ← 共享 Volume
↓ (Fluent Bit Sidecar 读取)
Fluent Bit Sidecar
├── 解析日志格式(JSON 结构化)
├── 添加 Pod 元数据标签
│ ├── namespace=production
│ ├── pod=nginx-abc123
│ └── app=web-server
└── 发送到 Elasticsearch
└── 写入速率:约 10,000 条/秒(单 Sidecar)
通过这种方式,日志收集不会阻塞主容器的正常运行。即使 Elasticsearch 不可用,Fluent Bit 会在本地缓存日志(默认最多 50MB),待 ES 恢复后再发送。
以一个典型的 Web 服务连接 PostgreSQL 为例,使用 Ambassador 模式管理数据库连接池:
| 配置参数 | 无 Ambassador(直连) | 有 Ambassador(代理) |
|---|---|---|
| 最大连接数 | 应用配置 200 | Ambassador 池 100 |
| 实际活跃连接 | 120-180(应用峰值) | 60-85(池复用) |
| 连接创建开销 | 每次新建 | 预热后 |
| DB 端连接数 | 200(可能打满) | 100(可控) |
| P99 查询延迟 | 250 ms | 120 ms |
| 连接泄漏风险 | 高(应用 Bug 导致) | 低(Ambassador 可回收) |
连接池大小的计算公式:
以一个 4 核 CPU、平均查询耗时 50ms(其中等待 I/O 30ms)的应用为例:
实际上还需要考虑波动,通常取计算值的 2-3 倍:
这与直接设置 max_connections=100 的做法形成鲜明对比——Ambassador 模式下,我们实际只需要 ~8 个连接即可。
正常 (Closed)
│
失败率 > 阈值 (如 50%) → 开启熔断
│
熔断 (Open)
│
┌────────┴────────┐
│ 超时后部分放行 │
│ │
▼ ▼
半开 (Half-Open) 继续失败
│ │
成功率达到阈值 ──────→ 再次熔断
│
▼
关闭熔断 (Back to Closed)
熔断器关键参数公式:
实际例子:一个每秒处理 1000 请求的服务,错误率突增到 60%:
假如一个微服务集群中有 5 种不同类型的服务,各自输出不同的监控格式:
| 服务类型 | 原始格式 | 关键指标数量 | 格式特点 | Adapter 转换耗时 |
|---|---|---|---|---|
| Web API | JSON | 12 个 | {"requests": 100} |
0.3 ms |
| 消息队列 | Text | 8 个 | rate=50 errors=2 |
0.5 ms |
| 数据库 | StatsD | 20 个 | db.queries:100|ms |
0.2 ms |
| 缓存层 | Prometheus | 15 个 | cache_hits_total{type="mem"} 5000 |
0.1 ms |
| 批处理 | CSV 文件 | 6 个 | 2026-05-29,1000,10,0.01 |
1.2 ms |
统一转换为 Prometheus 格式后,Adapter 在 Prometheus 抓取(每 15 秒一次)时完成转换,CPU 开销约 5-15 mCPU/服务,内存开销约 5-20 MB/服务。
这一层次的模式解决多个 Pod/节点之间的协作问题。
k8s.io/client-go/tools/leaderelection 包Leader Election 的核心分布式锁机制基于租约(Lease)概念:
租约(Lease):Leader 持有租约(TTL 时间内有效),定期刷新。
选举时间分析:
etcd 选举性能实测数据:
| etcd 集群大小 | TTL | 平均选举时间 | 最大选举时间 | 脑裂窗口 | 选举期间读请求 |
|---|---|---|---|---|---|
| 3 节点 | 5s | 1.2s | 3.8s | ~0s | 失败(不可用) |
| 3 节点 | 15s | 3.5s | 10.2s | ~0s | 失败 |
| 5 节点 | 5s | 1.5s | 4.1s | ~0s | 失败 |
| 5 节点 | 15s | 4.2s | 11.5s | ~0s | 失败 |
| 7 节点 | 5s | 1.6s | 4.5s | ~0s | 失败 |
关键结论:
from kubernetes import client, config
import uuid
config.load_incluster_config()
lease_client = client.CoordinationV1Api()
namespace = "default"
lease_name = "scheduler-leader"
pod_name = f"scheduler-{uuid.uuid4().hex[:8]}"
def try_acquire_lease():
"""尝试获取租约成为 Leader"""
try:
lease = lease_client.read_namespaced_lease(lease_name, namespace)
# 检查租约是否过期
now = datetime.utcnow()
if lease.spec.lease_duration_seconds and lease.spec.renew_time:
renew_time = lease.spec.renew_time
expire_time = renew_time + timedelta(seconds=lease.spec.lease_duration_seconds)
if now < expire_time and lease.spec.holder_identity != pod_name:
return False # 当前被其他人持有
# 尝试获取/更新租约
lease.spec.holder_identity = pod_name
lease.spec.lease_duration_seconds = 15
lease.spec.acquire_time = now
lease.spec.renew_time = now
lease_client.replace_namespaced_lease(lease_name, namespace, lease)
return True
except ApiException as e:
if e.status == 404:
# 创建新的租约
lease_body = ... # 创建 Lease 对象
lease_client.create_namespaced_lease(namespace, lease_body)
return True
return False
# 主循环
while True:
if try_acquire_lease():
# 作为 Leader 执行任务
pass
time.sleep(1)
| 指标 | RabbitMQ | Apache Kafka | Redis Streams | AWS SQS |
|---|---|---|---|---|
| 单节点吞吐量 | 10,000 msg/s | 500,000 msg/s | 100,000 msg/s | 无限(弹性) |
| 消息持久化 | 磁盘 + 内存 | 磁盘(追加写) | 磁盘(RDB/AOF) | 自动 |
| 消息顺序保证 | 单队列有序 | 分区内有序 | 组内有序列 | Best-effort |
| 消费确认 | ACK/NACK | Offset Commit | XACK | Delete 后 |
| 延迟 P50 | 20-100 μs | 2-10 ms | 0.1-1 ms | 10-100 ms |
| 延迟 P99 | 100-500 μs | 10-50 ms | 1-5 ms | 100-500 ms |
| 死信队列 | 支持 | 通过 Topic | XGROUP | DLQ |
| 运维复杂度 | 中 | 高(ZooKeeper) | 低 | 无(托管) |
| 适用队列深度 | 百万级 | 十亿级 | 千万级 | 无限 |
使用 Little's Law 计算系统所需 Worker 数量:
其中:
实际例子:一个订单处理系统
所需最小 Worker 数:
其中 为目标资源利用率(通常 留余量):
如果队列深度突然从 100 增长到 10,000,说明 Worker 处理能力不足或下游系统故障。按照指数增长曲线,当系统 的 Worker 故障时:
假设 , (10% Worker 故障),队列深度会在 内从 100 飙升至 ~800。
| 策略 | 描述 | 总响应时间 | 数据完整率 | 适用场景 |
|---|---|---|---|---|
| WaitAll | 等待所有分片完成 | 100% | 金融结算、数据完整性要求高 | |
| WaitN | 等待先完成的 N 个 | 80-95% | 搜索引擎(Top-K 结果足够) | |
| WaitFirst | 取第一个完成的结果 | ~20% | 缓存查询(任意副本可用即可) | |
| WaitWithTimeout | 超时后返回已有结果 | 弹性 | 通用策略 |
最优超时计算:假设有 个后端分片,每个分片的响应时间服从 ,期望在 概率下收集到 个结果:
具体量化示例:
搜索引擎有 100 个分片,每个分片响应时间 ,希望在 95% 的情况下收集到至少 80 个结果:
注意 ,对应的 :
即在 95% 的情况下,设置 116ms 的超时可以收集到至少 80 个分片的结果(数据完整性 80%)。如果要求 100% 完整率,超时需要设置到 左右。
Hugo 的实践经验:在微服务架构中,Scatter/Gather 容易遇到 N+1 问题。如果每次都 Scatter 到所有后端,请求量会随服务数量线性增长。建议:
假设一个系统有 个分片,每个请求发往所有分片,请求率为 req/s:
| 分片数 N | 请求率 R | 总请求率 | 网络带宽消耗 | 内存消耗(连接池) |
|---|---|---|---|---|
| 10 | 100/s | 1,000/s | 500 KB/s | 20 MB |
| 50 | 100/s | 5,000/s | 2.5 MB/s | 100 MB |
| 100 | 100/s | 10,000/s | 5 MB/s | 200 MB |
| 500 | 100/s | 50,000/s | 25 MB/s | 1 GB |
| 1000 | 100/s | 100,000/s | 50 MB/s | 2 GB |
优化方案:并非所有请求都需要 Scatter 到全部分片。可以引入多级 scatter:
这种策略可以将平均资源消耗降低 60-80%。
| 运行环境 | 冷启动耗时 | 温启动耗时 | 内存占用 | 冷启动频率(典型) |
|---|---|---|---|---|
| AWS Lambda (Node.js) | 200-600 ms | 1-10 ms | 128 MB-10 GB | 每 5-15 min |
| AWS Lambda (Python) | 300-800 ms | 1-10 ms | 128 MB-10 GB | 每 5-15 min |
| AWS Lambda (Java) | 3-10 s | 1-10 ms | 512 MB-10 GB | 每 5-15 min |
| Google Cloud Functions | 300-700 ms | 5-20 ms | 128 MB-2 GB | 每 10-30 min |
| Azure Functions | 200-800 ms | 5-15 ms | 128 MB-1.5 GB | 每 5-20 min |
| Knative (Kubernetes) | 2-10 s | 10-50 ms | 自定义 | 每 15-60 min |
冷启动优化技术对比:
| 技术 | 延迟降低 | 额外成本 | 复杂度 |
|---|---|---|---|
| 预置并发(Provisioned Concurrency) | 90-95% | 高(按实例收费) | 低 |
| 快照启动(SnapStart/Snapshot) | 60-80% | 无 | 低 |
| Keep-Warm Ping(定时触发) | 30-50% | 低(请求费) | 低 |
| 函数合并(减少函数数量) | 20-40% | 无 | 中 |
| 轻量运行时(Bun, QuickJS) | 40-60% | 无 | 高 |
分布式系统中最难处理的就是状态。这本书详细讲解了:
Redis/Memcached 的切片集群
Redis Cluster 使用一致性哈希实现数据分布,但采用了更稳定的**哈希槽(Hash Slot)**方案——将整个 Key 空间分为 16384 个槽,每个节点负责一部分槽。
哈希槽的计算方式:
数据分布示例(假设 3 节点集群):
| Key | CRC16 | Slot | 归属节点 |
|---|---|---|---|
user:1001 |
0xA3F1 | 41969 mod 16384 = 12001 | Node 3 |
user:1002 |
0x7B42 | 31554 mod 16384 = 15170 | Node 3 |
order:50001 |
0x1CD8 | 7384 mod 16384 = 7384 | Node 1 |
order:50002 |
0xE5A9 | 58793 mod 16384 = 14441 | Node 2 |
session:abc |
0x2903 | 10499 mod 16384 = 10499 | Node 2 |
缓存策略对比:
| 策略 | 读性能 | 写性能 | 一致性 | 实现复杂度 | 典型场景 |
|---|---|---|---|---|---|
| Cache-Aside | 高 | 中 | 最终一致性 | 低 | 通用场景(最常用) |
| Read-Through | 高 | 中 | 最终一致性 | 中 | 标准数据访问层 |
| Write-Through | 中 | 低 | 强一致性 | 中 | 需要数据完整性的场景 |
| Write-Behind | 中 | 高 | 弱一致性 | 高 | 高写入吞吐场景 |
| Write-Around | 高 | 高 | 最终一致性 | 低 | 冷数据多、写频繁 |
缓存穿透、击穿、雪崩量化分析:
| 问题 | 现象 | 数据库影响 | 解决方案 | 效果量化 |
|---|---|---|---|---|
| 穿透 | 查询不存在的数据,每次穿透到 DB | DB QPS 从 100 提升到 10,000 | 缓存空值(TTL=60s) | 99% 穿透请求被拦截 |
| 击穿 | 热点 Key 过期,大量请求同时打 DB | DB QPS 瞬间 100→10,000 | 互斥锁(SETNX)+ 回源限流 | 回源 QPS 从 10,000 降到 1 |
| 雪崩 | 大量 Key 同时过期 | DB QPS 雪崩式增长 | TTL 加随机偏移(base ± random) | 防止 Key 同时过期 |
Hugo 的实践建议:缓存失效是"计算机科学的两大难题之一"。建议:
主从复制(Master-Slave)
三种复制模式的数据对比:
| 指标 | 异步复制 | 半同步复制(1 个从节点) | 全同步复制 |
|---|---|---|---|
| 写入延迟 | |||
| 典型延迟 | 0.1-1 ms | 1-5 ms(同机房) | 2-20 ms |
| 数据安全 | 主节点崩溃可能丢数据 | 最多丢 0-1 个事务 | 无数据丢失 |
| 可用性 | 高(主节点独立工作) | 中(从节点确认增加等待) | 低(从节点不可用阻塞写入) |
| 适用场景 | 缓存、日志 | 通用 OLTP 数据库 | 金融交易、账户系统 |
多主复制(Multi-Master)
CRDT 举例:计数器(G-Counter)
每个节点维护自己的增量,合并时取各节点最大值之和:
假设三个节点,各自累加:
| 操作 | Node 1 | Node 2 | Node 3 | 合并结果 |
|---|---|---|---|---|
| 初始 | 0 | 0 | 0 | 0 |
| Node1 +5 | 5 | 0 | 0 | 5 |
| Node2 +3 | 5 | 3 | 0 | 8 |
| Node3 +10 | 5 | 3 | 10 | 18 |
| 网络分区后合并 | 5 | 3 | 10 | 18(无冲突) |
水平分片 vs 垂直分片
| 维度 | 水平分片 | 垂直分片 |
|---|---|---|
| 分片依据 | 行(按 Key 分布) | 列(按功能模块) |
| 数据量扩展 | 线性扩展 | 有限扩展 |
| 跨分片查询 | 困难(需要 Scatter/Gather) | 可能跨模块 |
| 热点问题 | 分片键选择不当会热点 | 模块间负载不均 |
| 典型场景 | 用户数据分片 | 按业务模块分库 |
分片键选择示例:
| 分片键 | 数据分布均匀性 | 查询局部性 | 未来扩展性 | 综合评价 |
|---|---|---|---|---|
| user_id(哈希) | ⭐⭐⭐⭐⭐ 完美均匀 | ⭐⭐ 单用户查询好,范围查询差 | ⭐⭐⭐⭐ 易扩容 | 推荐 |
| order_id(哈希) | ⭐⭐⭐⭐⭐ 完美均匀 | ⭐⭐ 同上 | ⭐⭐⭐⭐ 易扩容 | 推荐 |
| 时间戳(按天) | ⭐ 写入热点(最新一天) | ⭐⭐⭐⭐⭐ 时间范围查询好 | ⭐⭐ 数据倾斜 | 不推荐 |
| 地理位置 | ⭐⭐ 可能热点(一线城市多) | ⭐⭐⭐⭐ 地理查询好 | ⭐⭐ 大城市数据膨胀 | 慎用 |
| 用户 ID 首字母 | ⭐⭐⭐ 字母分布不均 | ⭐⭐⭐ 前缀查询 | ⭐⭐ 扩容复杂 | 不推荐 |
Hugo 的经验:分片键选择是"一选定终身"的决策。建议:
MapReduce 的数学本质:
具体的 Word Count 示例:
输入文本:"分布式系统设计 分布式计算 系统架构"
Map 阶段输出:
("分布式", 1), ("系统", 1), ("设计", 1),
("分布式", 1), ("计算", 1), ("系统", 1), ("架构", 1)
Shuffle(按 Key 分组):
"分布式" → [1, 1]
"系统" → [1, 1]
"设计" → [1]
"计算" → [1]
"架构" → [1]
Reduce 阶段(求和):
"分布式" → 2
"系统" → 2
"设计" → 1
"计算" → 1
"架构" → 1
MapReduce 任务数据参考:
| 数据量 | Map 任务数 | Reduce 任务数 | 总处理时间 | 网络传输量 |
|---|---|---|---|---|
| 100 GB | 400 (256 MB/map) | 50 | ~5 min | ~10 GB |
| 1 TB | 4,000 | 200 | ~20 min | ~100 GB |
| 10 TB | 40,000 | 500 | ~1.5 h | ~1 TB |
| 100 TB | 400,000 | 1000 | ~6 h | ~10 TB |
| 1 PB | 4,000,000 | 2000 | ~24 h+ | ~100 TB |
数据本地性优化效果:将计算调度到数据所在节点,可避免 70-90% 的网络数据传输。
容错机制:假设系统 MTBF(平均无故障时间)= 24h,一个 5 小时的 1 TB 任务期间,1000 个节点中有 1-2 个节点故障概率约为 20%。MapReduce 的容错策略是重新执行失败的任务(而非整个作业),每次故障只丢失 ~2-3 分钟的工作进度。
这本书虽然没有专门章节讲,但贯穿全书的设计哲学:
| 权衡维度 | 一致性优先 | 可用性优先 |
|---|---|---|
| 代表系统 | ZooKeeper, etcd | DynamoDB, Cassandra |
| CAP 选择 | CP | AP |
| 典型场景 | 配置、锁、Leader 选举 | 用户数据、会话存储 |
| 实现复杂度 | 低(强一致性简单) | 高(冲突解决复杂) |
| 运维难度 | 容易 | 较难 |
CAP 定理(Brewer's Theorem)指出分布式数据系统中 一致性(Consistency)、可用性(Availability)、分区容忍性(Partition Tolerance) 三者最多同时满足其二。
但实践中,更好的理解方式是 PACELC 模型(由 Daniel Abadi 于 2010 年提出):
在网络分区(Partition)时,在一致性(C)和可用性(A)之间选择;
在网络正常(Else)时,在延迟(Latency)和一致性(C)之间选择。
PACELC 模型的选择树:
┌─ 系统运行 ─┐
│
▼
┌─ 网络分区? ─┐
│ │
YES NO
│ │
▼ ▼
┌─ C vs A ─┐ ┌─ L vs C ─┐
│ │ │ │
▼ ▼ ▼ ▼
CP AP LC PC
│ │ │ │
(etcd, (Dynamo, (Cassandra (MySQL,
ZooKeeper) Cassandra) 本地读) PostgreSQL)
延迟 - 一致性权衡的量化数据:
| 系统 | 正常模式 | 写入延迟(P99) | 读取一致性 | 分区时行为 |
|---|---|---|---|---|
| etcd | PC | 5-15 ms | 线性一致性 | CP(暂停写入) |
| ZooKeeper | PC | 5-20 ms | 线性一致性 | CP(暂停写入) |
| Cassandra | LC | 1-5 ms | 最终一致性 | AP(继续服务) |
| DynamoDB | LC | 5-15 ms | 最终/强一致可选 | AP(继续服务) |
| MySQL Group Replication | PC | 2-10 ms | 强一致性 | CP(从节点可读) |
| CockroachDB | PC | 30-100 ms(global) | 可串行化快照隔离 | CP(自动恢复) |
Brendan Burns 在书中反复强调的观点:
Burns 并非完全抛弃分布式事务,而是建议慎重使用。以下是常见方案的成本量化:
| 方案 | 实现复杂度 | 性能影响 | 数据一致性 | 适用数据量 | 典型案例 |
|---|---|---|---|---|---|
| 两阶段提交(2PC) | ⭐⭐⭐⭐ | 延迟增加 10-100x | 强一致 | 小(<10 节点) | 传统 XA 事务 |
| Saga(编排) | ⭐⭐⭐ | 延迟增加 2-3x | 最终一致 | 中等 | 微服务跨服务事务 |
| Saga( choreography) | ⭐⭐ | 延迟增加 1.5x | 最终一致 | 大 | 事件驱动架构 |
| TCC(Try-Confirm/Cancel) | ⭐⭐⭐⭐⭐ | 延迟增加 3-5x | 最终一致 | 中等 | 支付系统 |
| 本地消息表 | ⭐⭐ | 延迟增加 1.2x | 最终一致 | 大 | 订单系统 |
Saga 模式量化示例:一个跨 3 个服务的订单创建流程
CreateOrder(200ms) ─→ ReserveInventory(150ms) ─→ ProcessPayment(300ms)
│ │ │
▼ ▼ ▼
Compensate(50ms) ←── ReleaseReserve(50ms) ←── RefundPayment(100ms)
与 2PC 对比(相同场景):
这本书的每个模式都能在 Kubernetes 中找到对应实现:
| 模式 | Kubernetes 实现 | 典型 YAML/API |
|---|---|---|
| Sidecar | Pod 多容器 | spec.containers[0], spec.containers[1] |
| Ambassador | 本地代理容器 + localhost | 共享 localhost 网络命名空间 |
| Adapter | 日志/监控适配器 | DaemonSet 统一收集 |
| Leader Election | Endpoint 或 Lease API | k8s.io/client-go/tools/leaderelection |
| Work Queue | Job + Work Queue Controller | CRD 自定义资源 |
| Scatter/Gather | Service + 并行请求 | 应用层实现 |
| Stateful Service | StatefulSet | StatefulSet + PersistentVolumeClaim |
| Batch Processing | Job / CronJob | spec.completions, spec.parallelism |
| Service Discovery | Service + EndpointSlice | kube-proxy + CoreDNS |
Sidecar 模式:一个 Pod 中包含 Nginx 和 Fluent Bit 两个容器
apiVersion: v1
kind: Pod
metadata:
name: web-with-logging
spec:
volumes:
- name: logs
emptyDir: {}
containers:
- name: nginx
image: nginx:1.25
ports:
- containerPort: 80
volumeMounts:
- name: logs
mountPath: /var/log/nginx
- name: fluent-bit
image: fluent/fluent-bit:2.0
volumeMounts:
- name: logs
mountPath: /var/log/nginx
Leader Election:使用 Kubernetes 的 Lease API
apiVersion: coordination.k8s.io/v1
kind: Lease
metadata:
name: example-leader-lease
namespace: default
spec:
acquireTime: "2026-05-29T00:00:00Z"
holderIdentity: "example-pod-abc123"
leaseDurationSeconds: 15
leaseTransitions: 1
renewTime: "2026-05-29T00:00:10Z"
Stateful 服务:使用 StatefulSet 管理有状态服务
apiVersion: apps/v1
kind: StatefulSet
metadata:
name: redis-cluster
spec:
serviceName: redis
replicas: 3
selector:
matchLabels:
app: redis
template:
metadata:
labels:
app: redis
spec:
containers:
- name: redis
image: redis:7.0
ports:
- containerPort: 6379
volumeMounts:
- name: data
mountPath: /data
volumeClaimTemplates:
- metadata:
name: data
spec:
accessModes: ["ReadWriteOnce"]
resources:
requests:
storage: 100Gi
| 对比维度 | 本书 | 数据密集型应用系统设计 | 分布式系统模式 | 微服务设计 |
|---|---|---|---|---|
| 侧重点 | 模式化设计 | 存储系统原理 | 实现模式 | 微服务架构 |
| 深度 | 中(广度为主) | 深(每章都是博士级) | 中 | 中 |
| 代码示例 | 有(Kubernetes 相关) | 少 | 有(Go/C++) | 有 |
| 实战导向 | ⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐⭐ | ⭐⭐⭐⭐⭐ |
| 理论深度 | ⭐⭐⭐ | ⭐⭐⭐⭐⭐ | ⭐⭐⭐ | ⭐⭐⭐ |
| Kubernetes 覆盖 | ⭐⭐⭐⭐⭐ | ⭐ | ⭐⭐ | ⭐⭐⭐ |
| 推荐阅读顺序 | 第一本 | 第二本 | 第三本 | 第四本 |
第 1 步:《分布式系统应用设计》(本书)
→ 模式化思维,建立全景图(2-3周)
│
▼
第 2 步:《数据密集型应用系统设计》
→ 深入存储、复制、事务原理(2-3月)
│
├────────────────────┐
▼ ▼
第 3a 步:微服务架构设计模式 第 3b 步:分布式系统模式
→ 微服务专精 → 实现模式
│ │
▼ ▼
第 4 步:Kubernetes in Action + 实践(K8s 环境)
→ 将理论转化为实践
阅读本书后,可以通过以下实验巩固模式化思维:
目标:在本地 K8s 集群(minikube/kind)中创建一个包含 Sidecar 的 Pod
实验步骤:
预期时长:1-2 小时
目标:使用 etcd 或 Kubernetes Lease API 实现自定义 Controller 的 Leader Election
实验步骤:
leaderelection 包预期时长:2-3 小时
目标:实现一个简单的 Scatter/Gather 服务并测试不同超时策略的效果
实验步骤:
预期时长:3-4 小时
目标:对比 Redis Cluster 和直接数据库查询的性能差异
实验步骤:
预期时长:2-3 小时
Burns 在书中没有系统化讨论陷阱,但贯穿了相关思想。这里整理为表格:
| 陷阱 | 症状 | 根本原因 | 预防方法 |
|---|---|---|---|
| 网络不是可靠的 | 偶发超时、重试风暴 | 忽略网络分区可能性 | 所有远程调用必有超时 + 熔断 |
| 延迟不是零 | 系统响应变慢 | 假设网络延迟可忽略 | 设置合理超时,监控 P99 延迟 |
| 带宽不是无限的 | 传输大文件拖垮系统 | 忽略带宽瓶颈 | 使用压缩、分页、异步传输 |
| 拓扑不会保持不变 | 服务发现失败 | 硬编码 IP/地址 | 使用服务发现(DNS、Consul) |
| 只有一个管理员 | 配置错误导致故障 | 手动操作 | 基础设施即代码(IaC) |
| 传输开销为零 | 频繁调用导致性能差 | 忽略序列化/反序列化成本 | 批量操作、减少 RPC 调用 |
| 网络是同构的 | 跨区域延迟差异大 | 忽略地理分布 | 多区域部署、本地优先策略 |
传输开销的量化数据:
| 序列化格式 | 序列化时间 | 反序列化时间 | 数据大小(1KB 对象) | 网络传输时间(1Gbps) |
|---|---|---|---|---|
| JSON | 15 μs | 20 μs | 1.6 KB | 0.013 ms |
| Protocol Buffers | 5 μs | 8 μs | 0.8 KB | 0.006 ms |
| Avro | 6 μs | 10 μs | 0.9 KB | 0.007 ms |
| Thrift | 4 μs | 7 μs | 0.7 KB | 0.006 ms |
| FlatBuffers | 2 μs | 0.2 μs | 0.7 KB | 0.006 ms |
| MessagePack | 10 μs | 12 μs | 1.2 KB | 0.010 ms |
《分布式系统应用设计》是一本非常适合分布式系统入门的书籍。它采用模式化思维方式,将复杂的分布式系统设计拆解为可组合的、可复用的模式,配合 Kubernetes 实践环境,让读者快速建立分布式系统的全景认知。
最受启发的一点:模式化思维不只是技术方法,更是一种拆解复杂问题的思维方式。不管遇到什么分布式难题,先问自己三个问题:
把问题归类后,再去套对应的模式,设计就会清晰很多。
📚 读书笔记 | 架构师书架系列
书架定位:分布式系统入门首选。模式化思维给你一把钥匙,打开了分布式系统的大门。阅读建议:先读这本建立全景图,再读 DDIA 深入原理。