分布式理论
2026/1/15大约 3 分钟
分布式理论
CAP 理论
分布式系统不可能同时满足以下三个特性:
| 特性 | 说明 |
|---|---|
| C(Consistency) | 一致性,所有节点数据相同 |
| A(Availability) | 可用性,每个请求都能得到响应 |
| P(Partition tolerance) | 分区容错性,网络分区时系统仍能运行 |
CAP 选择
由于网络分区不可避免,实际上是在 CP 和 AP 之间选择。
| 选择 | 说明 | 示例 |
|---|---|---|
| CP | 保证一致性,牺牲可用性 | Zookeeper、HBase |
| AP | 保证可用性,牺牲一致性 | Eureka、Cassandra |
CAP 示例
CP 选择:节点 B 拒绝服务,等待与 A 同步
AP 选择:节点 B 继续服务,可能返回旧数据
BASE 理论
对 CAP 中 AP 的延伸,是大规模分布式系统的实践总结。
| 特性 | 说明 |
|---|---|
| BA(Basically Available) | 基本可用,允许部分功能降级 |
| S(Soft State) | 软状态,允许中间状态存在 |
| E(Eventually Consistent) | 最终一致性,最终数据一致 |
最终一致性
数据更新后,经过一段时间,所有节点数据最终一致。
实现方式:
- 读时修复
- 写时修复
- 异步修复
一致性模型
强一致性
写操作完成后,所有读操作都能读到最新值。
弱一致性
写操作完成后,不保证能读到最新值。
最终一致性
写操作完成后,经过一段时间,最终能读到最新值。
因果一致性
有因果关系的操作保证顺序。
读己之写
自己写的数据,自己能立即读到。
单调读
读到某个值后,不会读到更旧的值。
单调写
写操作按顺序执行。
一致性协议
Paxos
分布式一致性算法的基础。
角色:
- Proposer:提案者
- Acceptor:接受者
- Learner:学习者
两阶段:
- Prepare 阶段:获取提案编号
- Accept 阶段:提交提案
Raft
更易理解的一致性算法。
角色:
- Leader:领导者
- Follower:跟随者
- Candidate:候选者
核心机制:
- 领导选举
- 日志复制
- 安全性
ZAB
Zookeeper 使用的一致性协议。
两种模式:
- 恢复模式:选举 Leader
- 广播模式:同步数据
分布式 ID
UUID
String uuid = UUID.randomUUID().toString();
// 550e8400-e29b-41d4-a716-446655440000优点:简单,无需协调
缺点:无序,不适合做主键
雪花算法
0 - 41位时间戳 - 10位机器ID - 12位序列号public class SnowflakeIdGenerator {
private long workerId;
private long sequence = 0L;
private long lastTimestamp = -1L;
public synchronized long nextId() {
long timestamp = System.currentTimeMillis();
if (timestamp == lastTimestamp) {
sequence = (sequence + 1) & 4095;
if (sequence == 0) {
timestamp = waitNextMillis(lastTimestamp);
}
} else {
sequence = 0L;
}
lastTimestamp = timestamp;
return ((timestamp - 1609459200000L) << 22)
| (workerId << 12)
| sequence;
}
}优点:有序,高性能
缺点:依赖时钟
数据库自增
-- 单独的 ID 表
CREATE TABLE id_generator (
id BIGINT AUTO_INCREMENT PRIMARY KEY,
stub CHAR(1) NOT NULL
);
REPLACE INTO id_generator (stub) VALUES ('a');
SELECT LAST_INSERT_ID();Redis 自增
Long id = redisTemplate.opsForValue().increment("id:order");号段模式
// 每次从数据库获取一段 ID
// 如:获取 1000-1999,用完再获取 2000-2999分布式限流
计数器
// 固定窗口
if (count.incrementAndGet() > limit) {
return false;
}问题:临界点突发流量
滑动窗口
// 将窗口分成多个小窗口
// 统计最近 N 个小窗口的请求数漏桶算法
// 请求进入桶中,以固定速率流出
// 桶满则拒绝令牌桶算法
// 以固定速率生成令牌
// 请求需要获取令牌才能执行
// 允许突发流量
RateLimiter rateLimiter = RateLimiter.create(100); // 每秒100个令牌
if (rateLimiter.tryAcquire()) {
// 执行请求
}负载均衡
轮询
int index = counter.getAndIncrement() % servers.size();
return servers.get(index);加权轮询
// 根据权重分配请求
// 权重高的服务器处理更多请求随机
int index = random.nextInt(servers.size());
return servers.get(index);一致性哈希
// 将服务器映射到哈希环上
// 请求根据 key 的哈希值找到最近的服务器
// 增减服务器只影响相邻节点最少连接
// 选择当前连接数最少的服务器