Skip to content

Commit d495106

Browse files
committed
docs: 添加分布式协调机制和锁机制使用指南文档
- 新增《租约机制和其它分布式协调框架》文档,系统梳理分布式协调机制 - 新增《锁机制使用指南》文档,提供多语言锁机制实现示例 - 两篇文档共同构成后端分布式系统并发控制参考指南
1 parent 62ba687 commit d495106

2 files changed

Lines changed: 246 additions & 0 deletions

File tree

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
这个问题非常好!分布式场景下的协调机制远不止"分布式锁"一种。Lease(租约)确实是其中非常核心的一种,但它和"锁"既有联系又有本质区别。
2+
3+
我们来系统性地梳理一下分布式场景中**所有主流的协调机制**
4+
5+
---
6+
7+
## 一、 Lease(租约)机制
8+
9+
### 它和分布式锁是什么关系?
10+
11+
**Lease 是分布式锁的底层灵魂。** 准确地说:
12+
13+
> **分布式锁 = Lease(租约)+ 资源绑定(锁名)+ 释放协议**
14+
15+
一个纯粹的 Lease 只关心一件事:**"我在一段时间内拥有某种权利,到期自动作废"**
16+
而分布式锁是 Lease 的一种具体应用:把这个"权利"绑定到了"独占某个资源"上。
17+
18+
### Lease 的核心特征
19+
20+
```
21+
┌──────────────────────────────────────────────┐
22+
│ Lease 租约模型 │
23+
│ │
24+
│ 持有者: Node-A │
25+
│ 租约内容: "我有权处理 order-1001" │
26+
│ 有效期: 10秒 (TTL) │
27+
│ 到期行为: 自动失效,无需手动释放 │
28+
│ │
29+
│ 续约(Renew): 持有者可以在到期前主动续期 │
30+
│ 撤销(Revoke): 中央协调者可以强制撤销 │
31+
└──────────────────────────────────────────────┘
32+
```
33+
34+
### Lease vs Lock 的本质区别
35+
36+
| 对比维度 | 传统分布式锁 | Lease 租约 |
37+
| :--- | :--- | :--- |
38+
| **生命周期** | 必须显式释放(`unlock()`| **到期自动失效**,天然防死锁 |
39+
| **续期机制** | 可选(如 Redisson Watchdog) | **续约是核心设计**(心跳续约) |
40+
| **持有者崩溃** | 如果没设超时,可能永久死锁 | **自动过期释放**,其他节点可接管 |
41+
| **典型用途** | 保护临界区(如扣库存) | Leader 选举、服务注册、缓存一致性 |
42+
43+
### Lease 在实际系统中的应用
44+
45+
| 系统 | 怎么用 Lease 的 |
46+
| :--- | :--- |
47+
| **Kubernetes** | 每个 Pod 通过 Lease 对象向 API Server 上报心跳,**到期未续约则判定节点死亡**,触发 Pod 驱逐重调度 |
48+
| **Zookeeper** | 客户端与 ZK 建立 Session,本质就是一个 Lease。Session 过期 → 临时节点自动删除 → 触发 Watcher 通知 |
49+
| **etcd** | 通过 `Grant(TTL)` 创建租约,绑定到 Key 上。**Leader 选举的底层核心就是 Lease** |
50+
| **Redis** | `SET key value EX 10 NX` 本质就是一个 10 秒的 Lease |
51+
| **HDFS** | NameNode 给 DataNode 颁发 Lease,**持有 Lease 的客户端独占写文件权** |
52+
53+
---
54+
55+
## 二、 分布式场景中的其他常用协调机制
56+
57+
除了"锁"和"租约",分布式系统中还有一整套工具箱:
58+
59+
---
60+
61+
### 1. Leader Election(领导者选举)
62+
63+
**解决的问题**:集群中多个节点,必须选出**唯一一个节点**来执行某项工作(比如定时任务调度、数据分片协调)。
64+
65+
**底层原理**:通常就是基于 Lease 实现的!谁先抢到 Lease 谁就是 Leader,Leader 持续续约保持身份,一旦 Leader 崩溃、Lease 到期,其他节点重新竞选。
66+
67+
```
68+
┌─────────────────────────────────────────────┐
69+
│ Leader Election 流程 │
70+
│ │
71+
│ Node-A ──► 尝试写入 /election/leader ──► 成功!成为 Leader
72+
│ Node-B ──► 尝试写入 /election/leader ──► 失败,Watch 等待
73+
│ Node-C ──► 尝试写入 /election/leader ──► 失败,Watch 等待
74+
│ │
75+
│ [Leader 崩溃] ──► Lease 到期 ──► Key 被删除 │
76+
│ Node-B ──► 抢到写入 ──► 成为新 Leader │
77+
└─────────────────────────────────────────────┘
78+
```
79+
80+
**实际应用**
81+
82+
| 系统 / 框架 | 怎么做 Leader 选举 |
83+
| :--- | :--- |
84+
| **Kubernetes** Controller Manager | 通过 **Lease 对象** 竞选,只有 Leader 实例工作 |
85+
| **Elasticsearch** | 通过内部共识协议选举 Master 节点 |
86+
| **Kafka** | Controller Broker 通过 Zookeeper 临时节点竞选 |
87+
| **自研业务** | 用 Redis `SETNX` 或 etcd `CreateRevision` 实现 |
88+
89+
---
90+
91+
### 2. Fencing Token(隔离令牌 / 防护令牌)
92+
93+
**解决的问题**:分布式锁的一个致命漏洞 —— **锁过期了,但持有者还不知道**
94+
95+
```
96+
时间线:
97+
t1: Node-A 获取锁 (TTL=10s)
98+
t2: Node-A 发生 Full GC 停顿 30 秒...(它以为自己还持有锁)
99+
t3: 锁自动过期,Node-B 成功获取锁
100+
t4: Node-A 从 GC 中醒来,以为自己还有锁,继续写数据库 ← 💥 脑裂!
101+
```
102+
103+
**Fencing Token 的解决方案**
104+
每次获取锁时,锁服务会颁发一个**全局单调递增的令牌号(epoch / revision)**
105+
下游资源(数据库、存储系统)会**拒绝接受过期令牌号的写入请求**
106+
107+
```
108+
Node-A 获取锁 → 得到 Token #35
109+
Node-B 获取锁 → 得到 Token #36
110+
Node-A 醒来,拿着 Token #35 去写数据库
111+
数据库: "我上次接受的是 #36,你的 #35 过时了,拒绝!" ← ✅ 安全
112+
```
113+
114+
**实际应用**:etcd 的 Revision、Zookeeper 的 zxid、Google Chubby 的 sequencer。
115+
116+
---
117+
118+
### 3. Idempotency(幂等性设计)
119+
120+
**解决的问题**:网络超时后重试、消息队列重复投递、用户狂点按钮。**不是阻止并发,而是让重复执行也不会出错**
121+
122+
**核心思路**:每个操作携带一个**唯一的请求ID(幂等键)**,服务端记录已经处理过的 ID,重复请求直接返回上次的结果。
123+
124+
```python
125+
# Python 伪代码
126+
def create_order(idempotency_key: str, data: dict):
127+
# 第一步:检查这个 key 是否已经处理过
128+
existing = redis.get(f"idem:{idempotency_key}")
129+
if existing:
130+
return json.loads(existing) # 直接返回上次结果,不再执行
131+
132+
# 第二步:正常执行业务
133+
result = do_create_order(data)
134+
135+
# 第三步:记录已处理,设置过期时间
136+
redis.set(f"idem:{idempotency_key}", json.dumps(result), ex=3600)
137+
return result
138+
```
139+
140+
**实际应用**
141+
142+
- 支付接口(支付宝、Stripe)都要求传 `out_trade_no` 作为幂等键
143+
- 消息队列消费者的"去重表"
144+
- HTTP 的 `Idempotency-Key` Header 标准
145+
146+
---
147+
148+
### 4. Distributed Transaction(分布式事务)
149+
150+
**解决的问题**:一个业务操作需要修改多个不同服务/数据库的数据,要保证**要么全成功,要么全回滚**
151+
152+
| 方案 | 核心思路 | 一致性 | 常用框架 |
153+
| :--- | :--- | :--- | :--- |
154+
| **2PC (两阶段提交)** | 协调者先问所有参与者"能不能提交?",全部同意后再统一提交。 | 强一致 | MySQL XA、Seata AT模式 |
155+
| **TCC (Try-Confirm-Cancel)** | 业务层面拆成三步:预留资源 → 确认 → 取消。 | 强一致 | Seata TCC模式、Hmily |
156+
| **Saga (编排/协作)** | 把大事务拆成一系列小事务,每个小事务都有对应的**补偿回滚操作**| 最终一致 | Temporal、Cadence、自研 |
157+
| **本地消息表** | 业务操作和消息写入在同一个本地事务中,由后台任务异步投递消息。 | 最终一致 | 自研(非常实用) |
158+
159+
```
160+
Saga 举例 (下单流程):
161+
┌────────────┐ ┌────────────┐ ┌────────────┐
162+
│ 1.创建订单 │───►│ 2.扣减库存 │───►│ 3.扣减余额 │
163+
│ 补偿:取消订单│◄───│ 补偿:恢复库存│◄───│ 补偿:退款 │ ← 任何一步失败,
164+
└────────────┘ └────────────┘ └────────────┘ 反向执行补偿操作
165+
```
166+
167+
---
168+
169+
### 5. CAS (Compare-And-Swap / 乐观并发控制)
170+
171+
**解决的问题**:无锁并发!不加锁,而是在**更新的瞬间检查数据是否被别人改过**
172+
173+
这不仅仅用在数据库(乐观锁 `version`),在分布式协调系统中也是核心原语:
174+
175+
| 系统 | CAS 的具体表现 |
176+
| :--- | :--- |
177+
| **etcd** | `Txn(If(key.Version == X).Then(Put(...)))` |
178+
| **Zookeeper** | `setData(path, data, expectedVersion)` |
179+
| **DynamoDB** | `ConditionExpression: "version = :v"` |
180+
| **Consul** | `PUT /kv/key?cas=<ModifyIndex>` |
181+
182+
---
183+
184+
### 6. Watch / Pub-Sub(事件通知)
185+
186+
**解决的问题**:不轮询,而是**让状态变更主动通知到相关方**。常与 Lease、Leader 选举搭配使用。
187+
188+
```
189+
典型用法:
190+
Node-B watch "/election/leader"
191+
192+
[Leader Node-A 崩溃]
193+
→ etcd 检测到 Lease 到期
194+
→ 删除 Key
195+
→ 通知所有 Watcher
196+
→ Node-B 收到通知,立刻发起竞选
197+
```
198+
199+
| 系统 | 通知机制 |
200+
| :--- | :--- |
201+
| **etcd** | `Watch` API(长连接流式推送) |
202+
| **Zookeeper** | `Watcher`(一次性触发,需重新注册) |
203+
| **Redis** | `Pub/Sub` 频道 或 `Keyspace Notification` |
204+
| **Consul** | Long Polling(`?index=X&wait=30s`|
205+
206+
---
207+
208+
## 三、 全景总结
209+
210+
```
211+
┌─────────────────────────────────────────────────────────────┐
212+
│ 分布式协调机制全景图 │
213+
│ │
214+
│ ┌──────────┐ │
215+
│ │ Lease │──── 最底层的原语,带 TTL 的临时"凭证" │
216+
│ └────┬─────┘ │
217+
│ │ 基于 Lease 构建 │
218+
│ ├──► 分布式锁 (Lock) ← 独占资源 │
219+
│ ├──► 领导者选举 (Election) ← 独占角色 │
220+
│ └──► 服务注册/心跳 (Registry) ← 独占"我活着"的声明 │
221+
│ │
222+
│ ┌──────────┐ │
223+
│ │ CAS │──── 无锁乐观并发,"先不锁,改的时候再校验" │
224+
│ └──────────┘ │
225+
│ │
226+
│ ┌──────────┐ │
227+
│ │ Fencing │──── 防止锁过期后的脑裂,用单调递增 Token 隔离 │
228+
│ └──────────┘ │
229+
│ │
230+
│ ┌──────────┐ │
231+
│ │ 幂等性 │──── 不阻止重复,而是让重复无害 │
232+
│ └──────────┘ │
233+
│ │
234+
│ ┌──────────┐ │
235+
│ │ 分布式事务 │──── 跨服务的"全成功或全回滚" │
236+
│ │ 2PC/Saga │ │
237+
│ └──────────┘ │
238+
│ │
239+
│ ┌──────────┐ │
240+
│ │Watch/通知 │──── 状态变更的实时感知,驱动后续动作 │
241+
│ └──────────┘ │
242+
└─────────────────────────────────────────────────────────────┘
243+
```
244+
245+
**一句话记忆**
246+
> **Lease 是地基,Lock 和 Election 是房子,Fencing 是防盗门,CAS 是乐观派,幂等是佛系派,分布式事务是强迫症派,Watch 是通信兵。**

0 commit comments

Comments
 (0)