分布式系统设计面经
分布式核心知识
1. 分布式 ID 生成
| 方案 | 优点 | 缺点 |
|---|---|---|
| 数据库自增 | 简单有序 | 单点,性能差 |
| UUID | 无需协调 | 无序,存储空间大 |
| 雪花算法(Snowflake) | 高性能、趋势递增 | 依赖时钟,时钟回拨有风险 |
| 号段模式(Leaf) | DB 友好 | 需 DB,有号段浪费 |
| Redis INCR | 简单 | Redis 持久化有风险 |
雪花算法结构(64 bit)
1 bit (符号位=0) | 41 bit (时间戳ms) | 10 bit (机器ID) | 12 bit (序列号)
- 可用约 69 年,单机每毫秒 4096 个 ID
2. 分布式锁
Redis 实现
-- 加锁
SET lock_key unique_value NX EX 30
-- 释放锁(Lua 脚本保证原子性)
if redis.call('get', KEYS[1]) == ARGV[1] then
return redis.call('del', KEYS[1])
end
return 0
- 锁续期(看门狗):Redisson 默认 30s 过期,持锁期间每 10s 续期
- Redlock:向 N 个独立 Redis 实例加锁,超过半数成功则加锁成功
ZooKeeper 实现
- 创建临时有序节点,序号最小的获得锁
- 监听前一个节点删除事件,避免羊群效应
3. 分布式事务
2PC(两阶段提交)
阶段1:协调者发 Prepare → 各参与者执行事务(不提交)并返回 Yes/No
阶段2:全部 Yes → Commit;任一 No → Rollback
- 问题:协调者单点,阻塞协议,网络分区时数据不一致
TCC(Try-Confirm-Cancel)
- Try:预占资源(冻结库存)
- Confirm:确认提交(扣减库存)
- Cancel:取消回滚(解冻库存)
- 业务侵入性强,需实现三个接口
Saga
- 长事务拆成多个本地事务,每步有对应补偿事务
- 编排模式(Choreography)vs 指挥模式(Orchestration)
消息最终一致性
本地事务 + 发消息 → MQ → 消费者执行 → 失败则重试
- 使用事务消息(RocketMQ)保证发消息和本地事务原子性
4. 一致性哈希
- 将节点和数据 key 都映射到 0~2³² 的环上
- key 顺时针找到第一个节点
- 增减节点只影响相邻节点的数据迁移
- 虚拟节点:每个物理节点映射多个虚拟节点,负载更均衡
5. 分布式缓存架构
多级缓存
请求 → 本地缓存(Caffeine)→ 分布式缓存(Redis)→ 数据库
缓存与 DB 一致性策略
- Cache Aside:读:先缓存后 DB;写:先更新 DB,再删缓存
- Write Through:写 DB 和缓存同步进行(强一致但慢)
- 延迟双删:更新 DB → 删缓存 → 延迟 500ms 再删一次
6. 服务治理
熔断器状态机
Closed(正常)→ [失败率超阈值] → Open(熔断)
↓ [等待超时]
Half-Open(试探)
↓ [成功]
Closed
限流算法对比
| 算法 | 特点 |
|---|---|
| 固定窗口 | 简单,临界点突刺问题 |
| 滑动窗口 | 解决突刺,内存稍多 |
| 漏桶 | 平滑输出,不允许突发 |
| 令牌桶 | 允许一定突发,更常用 |
7. 高频面试题
- BASE 和 ACID 的关系? BASE 是 CAP 中 AP 的实践,用最终一致性换可用性
- 如何保证消息不丢失? 生产者确认 + MQ 持久化 + 消费者手动 ack
- 如何保证消息幂等? 消费端去重(唯一 ID + 数据库唯一约束 / Redis SET NX)
- Raft 和 Paxos 的区别? Raft 更易理解,强 Leader,leader 处理所有请求;Paxos 更通用复杂