分布式系统基础
问题
什么是 CAP 定理?分布式系统有哪些核心挑战?
答案
CAP 定理
在一个分布式系统中,以下三个特性最多只能同时满足两个:
| 字母 | 含义 | 说明 |
|---|---|---|
| C | Consistency(一致性) | 所有节点看到的数据一致 |
| A | Availability(可用性) | 每个请求都能收到响应 |
| P | Partition tolerance(分区容忍) | 网络分区时系统仍能运作 |
关键理解
分布式系统中网络分区不可避免(P 必须保留),所以实际选择是 CP 或 AP:
- CP:保证一致性,牺牲可用性(如 ZooKeeper、etcd)
- AP:保证可用性,牺牲一致性(如 Cassandra、DynamoDB)
BASE 理论
BASE 是对 CAP 中 AP 的补充:
| 概念 | 说明 |
|---|---|
| Basically Available | 基本可用,允许响应时间增加或功能降级 |
| Soft state | 软状态,允许数据存在中间状态 |
| Eventual consistency | 最终一致性,数据最终会达到一致 |
分布式 ID 生成
distributed-id.ts
// 方案对比
const idStrategies = {
uuid: {
pros: '简单、无中心依赖',
cons: '无序、索引性能差、36 字符太长',
},
snowflake: {
pros: '有序、高性能、信息丰富(时间+机器+序列)',
cons: '依赖时钟同步',
},
database: {
pros: '简单、有序',
cons: '单点瓶颈、性能低',
},
redis: {
pros: '高性能、有序',
cons: '依赖 Redis',
},
};
// 雪花算法简化版
class Snowflake {
private sequence = 0n;
private lastTimestamp = -1n;
constructor(
private workerId: bigint, // 机器 ID
private epoch = 1640995200000n, // 自定义纪元
) {}
nextId(): bigint {
let timestamp = BigInt(Date.now()) - this.epoch;
if (timestamp === this.lastTimestamp) {
this.sequence = (this.sequence + 1n) & 4095n; // 12 位序列号
if (this.sequence === 0n) {
// 同一毫秒序列号用完,等待下一毫秒
while (timestamp <= this.lastTimestamp) {
timestamp = BigInt(Date.now()) - this.epoch;
}
}
} else {
this.sequence = 0n;
}
this.lastTimestamp = timestamp;
return (timestamp << 22n) | (this.workerId << 12n) | this.sequence;
}
}
分布式锁
distributed-lock.ts
// Redis 分布式锁
class RedisLock {
async acquire(key: string, ttl: number = 10000): Promise<string | null> {
const token = crypto.randomUUID();
const result = await redis.set(
`lock:${key}`,
token,
'PX', ttl,
'NX', // 不存在时才设置
);
return result === 'OK' ? token : null;
}
async release(key: string, token: string): Promise<boolean> {
// Lua 脚本保证原子性:只有持有者才能释放
const script = `
if redis.call("get", KEYS[1]) == ARGV[1] then
return redis.call("del", KEYS[1])
else
return 0
end
`;
const result = await redis.eval(script, 1, `lock:${key}`, token);
return result === 1;
}
}
常见面试问题
Q1: 如何保证分布式系统的数据一致性?
答案:
- 强一致性:分布式事务(2PC),性能差
- 最终一致性:消息队列 + 重试 + 补偿,实际最常用
- 因果一致性:版本号/向量时钟
大多数互联网系统选择"最终一致性"。
Q2: 分布式锁有哪些实现方式?
答案:
| 方式 | 优点 | 缺点 |
|---|---|---|
| Redis(SET NX) | 高性能,简单 | 集群下有缺陷 |
| Redlock | 更可靠 | 实现复杂,有争议 |
| ZooKeeper | 强一致 | 重量级 |
| 数据库乐观锁 | 简单 | 性能低 |
Q3: 什么是脑裂?
答案:
网络分区导致集群分成两个独立的子集,各自认为对方挂了,各自选出新 Leader,导致两个 Leader 同时存在。解决方案:多数派选举(Raft/Paxos)、奇数节点部署。
Q4: 前端工程师为什么要了解分布式?
答案:
- 理解后端架构有助于 API 联调和问题排查
- BFF/Node.js 中间层面临分布式问题
- 前端也有分布式概念:多标签页状态同步、PWA 离线同步
- 面试高级前端经常问跨领域知识