系统设计与大规模架构学习笔记
从分布式理论到经典系统设计题,系统理解大规模互联网架构设计。
1 · 分布式基础理论
1.1 CAP 定理
CAP 定理指出,分布式系统最多同时满足以下三项中的两项:
- C(Consistency)一致性:所有节点同一时刻看到相同的数据
- A(Availability)可用性:每个请求都能收到非错误响应
- P(Partition Tolerance)分区容错性:网络分区时系统仍能继续运行
为什么 P 必须满足?
因为网络分区在分布式系统中不可避免(机器宕机、网线断开、机房故障),所以分布式系统实际上是在 C 和 A 之间做选择。
CP 系统:
- 优先保证一致性
- 网络分区时宁可拒绝服务
- 代表:ZooKeeper、etcd、HBase
AP 系统:
- 优先保证可用性
- 网络分区时允许暂时不一致
- 代表:Eureka、Cassandra、DynamoDB

1.2 BASE 理论
BASE 是 CAP 中 AP 方向的工程实践:
- Basically Available:基本可用,允许部分降级
- Soft State:软状态,允许中间状态
- Eventually Consistent:最终一致性,数据最终会变得一致
例如电商系统:下单后立即显示"待支付",库存稍后扣减,这就是最终一致性。
1.3 一致性模型
强一致性:
- 写入成功后,任何后续读取都能读到最新值
- 实现代价高
最终一致性:
- 写入后经过一段时间,所有副本一致
- 中间可能读到旧值
因果一致性:
读己之写(Read Your Writes):
2 · 共识算法
2.1 Raft
Raft 是一种易于理解的共识算法,用于在分布式系统中达成一致。
三种角色:
- Leader:处理所有客户端请求
- Follower:被动接收 Leader 日志
- Candidate:选举期间临时角色
Leader 选举:
- 每个节点随机超时(150ms ~ 300ms)
- 超时后成为 Candidate,给自己投票,向其他节点要票
- 获得多数票成为 Leader
日志复制:
- Leader 接收客户端请求,追加到本地日志
- Leader 并行发送给所有 Follower
- 多数 Follower 确认后提交
- Leader 通知 Follower 已提交
安全性:
- 已提交的日志不会丢失
- Leader 崩溃后,新 Leader 一定包含所有已提交日志
代表应用:etcd、Consul、TiKV。
2.2 Paxos
Paxos 是 Leslie Lamport 提出的经典共识算法。
角色:
- Proposer:提出提案
- Acceptor:对提案投票
- Learner:学习已通过的值
Paxos 分为两个阶段:Prepare 和 Accept。
Multi-Paxos 优化:选出一个稳定 Leader,减少两轮通信。
2.3 Raft vs Paxos
| 特性 |
Raft |
Paxos |
| 理解难度 |
低 |
高 |
| 工程实现 |
etcd / Consul |
Chubby / Spanner |
| 强 Leader |
是 |
可选 |
| 教学普及 |
广泛 |
偏理论 |
3 · 缓存策略
3.1 缓存模式
Cache-Aside(旁路缓存)
- 应用先读缓存,未命中再读 DB,然后写入缓存
- 最常用
def get_user(user_id):
user = cache.get(user_id)
if user is None:
user = db.query(user_id)
cache.set(user_id, user, ttl=3600)
return user
Read-Through
Write-Through
- 写入时同时更新缓存和 DB
- 数据一致性更好,但写延迟增加
Write-Behind(Write-Back)
3.2 缓存三大问题
缓存穿透
- 查询不存在的数据,请求直接打到 DB
- 解决:布隆过滤器、缓存空值
缓存击穿
- 热点 key 过期,大量请求同时查 DB
- 解决:互斥锁、热点 key 永不过期、随机过期时间
缓存雪崩
- 大量 key 同时过期
- 解决:过期时间加随机值、多级缓存、熔断限流
3.3 缓存淘汰策略
- LRU(Least Recently Used):最近最少使用
- LFU(Least Frequently Used):最不常用
- TTL(Time To Live):按过期时间淘汰
- Random:随机淘汰
4 · 限流算法
4.1 固定窗口计数器
把时间分成固定窗口,每个窗口计数。
窗口 1:0-1s,限制 100 请求
窗口 2:1-2s,限制 100 请求
缺点:窗口边界可能有 2 倍流量突刺。
4.2 滑动窗口
把窗口细分为多个小格子,滑动统计。
更平滑,但内存开销略大。
4.3 漏桶算法
请求像水滴一样进入桶,桶以固定速率漏水。
4.4 令牌桶算法
以固定速率往桶里放令牌,请求需要消耗令牌。
- 桶空时请求被拒绝或等待
- 允许突发流量(桶里积累了令牌)
- 最常用,Guava RateLimiter、Sentinel 都用它
// Guava 示例
RateLimiter limiter = RateLimiter.create(100.0); // 每秒 100 个
limiter.acquire(); // 获取令牌,阻塞等待
4.5 限流维度
- 全局限流:保护整个系统
- 用户级限流:每个用户 / IP 限制
- 接口级限流:不同接口不同阈值
- 分布式限流:用 Redis + Lua 脚本保证原子性
5 · 分库分表
5.1 为什么要分
单库单表的瓶颈:
- 数据量过大:查询慢,索引膨胀
- 并发写入:锁竞争
- 单表行数超过千万级性能下降明显
5.2 分片策略
范围分片:
- 按 ID 范围或时间范围
- 例如 user_0 存 1-1000万,user_1 存 1000万-2000万
- 优点:扩容简单
- 缺点:可能热点
哈希分片:
shard = hash(key) % N
- 数据均匀
- 扩容时需要重新分配数据
一致性哈希:
- 节点和数据都映射到哈希环上
- 增加/删除节点只影响相邻数据
- 减少扩容时的数据迁移量
5.3 分片键选择
分片键应该是查询最频繁的字段:
- 用户系统:user_id
- 订单系统:order_id 或 user_id(按用户查订单多)
要避免跨分片查询,否则需要聚合多个分片结果。
5.4 分库分表中间件
- ShardingSphere:Java 生态,支持分片、读写分离、加解密
- MyCAT:Proxy 层分片
- Vitess:YouTube 开源,MySQL 集群管理
5.5 分布式 ID
分库分表后自增 ID 不唯一,需要分布式 ID:
- UUID:简单,但无序、太长
- Snowflake:Twitter 开源,64 位,趋势递增
- Leaf:美团开源,号段模式 + Snowflake
- 数据库号段:从数据库批量申请 ID 段
6 · 分布式锁
6.1 Redis 分布式锁
SET lock_key unique_value NX PX 30000
NX:只有 key 不存在时才设置
PX 30000:30 秒后自动释放,防止死锁
unique_value:释放时校验,防止误释放别人的锁
释放锁(Lua 脚本保证原子):
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
Redisson 看门狗:
- 锁快要过期但业务未完成时自动续期
- 避免业务执行时间长导致锁提前释放
6.2 ZooKeeper 分布式锁
- 创建临时顺序节点
- 序号最小的节点获得锁
- 其他节点 Watch 前一个节点
- 释放锁时删除节点 / 会话断开自动删除
优点:
缺点:
- 性能不如 Redis
- 依赖 ZooKeeper 集群
6.3 etcd 分布式锁
- 基于 Lease 租约自动释放
- 基于 Txn 事务原子比较版本号
resp, _ := kv.Txn(ctx).
If(clientv3.Compare(clientv3.CreateRevision("lock"), "=", 0)).
Then(clientv3.OpPut("lock", "held", clientv3.WithLease(leaseID))).
Else(clientv3.OpGet("lock")).
Commit()
7 · 经典系统设计题
7.1 设计短链服务
核心问题:把长 URL 转成短 URL。
方案:
- 发号器生成唯一 ID(自增 ID / Snowflake)
- 将 ID 转成 Base62(a-zA-Z0-9)
- 存储映射关系
- 用户访问短链时重定向到长 URL
存储:
短链生成方式:
- 自增 ID 转 Base62:简单、无冲突
- 哈希取前 8 位:可能冲突,需处理
7.2 设计消息队列
消息队列的核心功能:
存储设计:
- 顺序写磁盘(Kafka 用日志段)
- 零拷贝(sendfile)提高消费性能
可靠性:
代表:Kafka、RocketMQ、RabbitMQ、Pulsar。
7.3 设计 Feed 流
Feed 流 = 用户关注的人发布的内容按时间排序。
推模式(写扩散):
- 用户发内容时,推送到所有粉丝的收件箱
- 读时直接取自己的收件箱
- 适合粉丝少的场景
拉模式(读扩散):
- 用户读 Feed 时,实时拉取关注人的最新内容并排序
- 适合大 V 场景
混合模式:
存储:Redis Sorted Set,按时间戳排序。
7.4 设计秒杀系统
秒杀特点:
优化策略:
- 静态资源 CDN 化
- 页面倒计时、验证码、排队机制削峰
- 库存扣减前置到 Redis,避免直接打 DB
- 异步下单(消息队列)
- 限流、熔断、降级
数据库防超卖:
UPDATE stock SET count = count - 1 WHERE id = 1 AND count > 0;
7.5 设计分布式文件系统
参考 HDFS:
- NameNode:管理元数据
- DataNode:存储实际数据块
- 文件分成 128MB 的块
- 每个块默认 3 副本
- 机架感知放置副本
读写流程:
- 写:客户端联系 NameNode 获取 DataNode 列表,流水线写入多个副本
- 读:客户端从最近的 DataNode 读取
8 · 可观测性
8.1 三支柱
| 支柱 |
说明 |
工具 |
| 日志(Logging) |
离散事件记录 |
ELK、Loki |
| 指标(Metrics) |
聚合数值 |
Prometheus + Grafana |
| 链路追踪(Tracing) |
请求全链路 |
Jaeger、SkyWalking、Zipkin |
8.2 监控告警
关键指标:
- QPS(每秒请求数)
- 错误率
- P50 / P95 / P99 延迟
- CPU / 内存 / 磁盘 / 网络
- 业务指标:订单量、支付成功率
告警原则:
- 分级:P0(立即处理)~ P3(可延后)
- 避免告警风暴
- 每个告警要有处理手册
- 通知到人且不过度打扰
8.3 SLI / SLO / SLA
- SLI(Service Level Indicator):具体指标,如可用性 99.9%
- SLO(Service Level Objective):目标值
- SLA(Service Level Agreement):对用户的承诺,违约可能赔偿