mirror of
https://github.com/CyC2018/Interview-Notebook.git
synced 2026-10-31 19:13:42 +00:00
Update 分布式.md
补充事务消息先发半消息的原因与回查上限、Redisson 锁的推荐用法、ZAB 与 Raft 的恢复方式差异、本地消息表读取 binlog 的做法。
This commit is contained in:
+7
-3
@@ -91,7 +91,7 @@ COMMIT;
|
||||
|
||||
SETNX(SET if Not eXists)命令在键不存在时写入并返回 1,键已经存在时什么也不做并返回 0。它和数据库的唯一索引类似,可以用这个键是否存在来判断是否处于锁定状态。
|
||||
|
||||
EXPIRE 命令可以为键设置过期时间,从而避免了释放锁失败的问题。但是 SETNX 和 EXPIRE 是两条命令,不具有原子性,如果执行完 SETNX 之后进程崩溃,那么这个锁就永远不会过期。因此应该使用 SET 命令的 NX 和 PX 选项一次性完成加锁和设置过期时间:
|
||||
EXPIRE 命令可以为键设置过期时间,从而避免了释放锁失败的问题。但是 SETNX 和 EXPIRE 是两条命令,不具有原子性,如果执行完 SETNX 之后进程崩溃,那么这个锁就永远不会过期。因此应该使用 SET 命令的 NX 和 PX 选项一次性完成加锁和设置过期时间(Redis 2.6.12 起官方也把 SETNX 视为弃用):
|
||||
|
||||
```text
|
||||
SET lock_key unique_value NX PX 30000
|
||||
@@ -159,6 +159,8 @@ UPDATE inventory SET stock = 97, fence = 8 WHERE sku_id = 10086 AND fence <= 8;
|
||||
|
||||
RedLock 使用的随机值不是递增的,算法本身无法提供栅栏令牌;ZooKeeper 则可以使用加锁时创建的有序节点的序号或者事务 ID(zxid),它们都是单调递增的。
|
||||
|
||||
Redisson 已把 RedLock 标为弃用,改为推荐 RLock 加上主从同步检查;需要令牌时用 RFencedLock,它在加锁时对一个计数键执行 INCR,返回值作为令牌。这个计数键和锁在同一个主节点上,主从复制是异步的,故障转移后计数可能回退,令牌不再严格递增,所以它同样不能完全排除旧持有者的写入。
|
||||
|
||||
### ZooKeeper 的有序节点
|
||||
|
||||
#### 1. ZooKeeper 抽象模型
|
||||
@@ -318,12 +320,14 @@ Saga 没有预留资源的 Try 阶段,中间状态对其它事务可见,因
|
||||
|
||||
<div align="center"> <img src="pics/local-message-table.png" width="760px"/> </div><br>
|
||||
|
||||
实践中常在事务提交后立即异步发送一次,定时任务只转发超过一定时间仍未发送的消息,作为兜底;已发送的消息定期删除或归档。
|
||||
实践中常在事务提交后立即异步发送一次,定时任务只转发超过一定时间仍未发送的消息,作为兜底;已发送的消息定期删除或归档。消息表很大、扫描开销高时,也可以用 Canal 等工具读取 binlog,消息一写入就转发,但同样可能重复发送。
|
||||
|
||||
由于重新转发会导致消息被重复投递,积分服务要按订单号加事件类型去重,实现幂等,做法见 [接收端的可靠性](消息队列.md#接收端的可靠性)。订单服务的事务已经提交,无法回滚,积分服务执行失败时只能不断重试或者人工介入处理。所以它适合发券、加积分这类能接受最终一致、失败后可以重试的后续操作。
|
||||
|
||||
RocketMQ 的事务消息把本地消息表的工作交给了消息队列:生产者先发送一条对消费者不可见的半消息,再执行本地事务,然后根据执行结果通知 Broker 提交或者回滚这条消息。如果 Broker 长时间没有收到通知,会主动回查生产者的本地事务状态。
|
||||
|
||||
先发半消息,是为了先确认 Broker 可用,并让 Broker 一直持有这条消息:之后生产者无论在哪一步崩溃,Broker 都能通过回查得到结果。回查返回未知时会继续回查,默认最多 15 次,超过后半消息被转入系统主题 TRANS_CHECK_MAX_TIME_TOPIC,不再投递,需要人工处理。
|
||||
|
||||
<div align="center"> <img src="pics/rocketmq-transaction.png" width="600px"/> </div><br>
|
||||
|
||||
### 几种做法的对比
|
||||
@@ -530,7 +534,7 @@ Raft 与 Paxos 达成的结果是等价的,区别在于对 Leader 的依赖程
|
||||
- Paxos 中任何一个 Proposer 都可以发起提议,选出一个主 Proposer 只是性能上的优化(也用来避免上文的活锁)。Multi-Paxos 中日志的每个位置各自独立地确定,新的主节点还要先找回自己缺少的日志;
|
||||
- Raft 中选主是协议的一部分,只有 Leader 能追加日志,日志只从 Leader 流向 Follower,投票规则又保证当选的节点包含所有已提交的日志,新 Leader 不需要从其它节点补日志,因此更容易理解和实现。代价是所有写入都要经过 Leader,选举期间不能写入。
|
||||
|
||||
ZooKeeper 使用的 ZAB 协议同样以 Leader 为中心,纪元编号(epoch)的作用相当于 Raft 的任期号。
|
||||
ZooKeeper 使用的 ZAB 协议同样以 Leader 为中心,纪元编号(epoch)的作用相当于 Raft 的任期号。不同的是恢复方式:ZAB 选出见过最大 zxid 的节点当 Leader,新 Leader 先同步日志,使包括自己在内的大多数节点状态一致,再开始处理新的写请求;Raft 不设单独的同步阶段,靠追加日志时的一致性检查边复制边覆盖。ZAB 还假设节点之间是 TCP 这样的先进先出通道。
|
||||
|
||||
## 七、RPC 🔥
|
||||
|
||||
|
||||
Reference in New Issue
Block a user