diff --git a/notes/分布式.md b/notes/分布式.md index 996d5fea..01dfa3dd 100644 --- a/notes/分布式.md +++ b/notes/分布式.md @@ -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 阶段,中间状态对其它事务可见,因

-实践中常在事务提交后立即异步发送一次,定时任务只转发超过一定时间仍未发送的消息,作为兜底;已发送的消息定期删除或归档。 +实践中常在事务提交后立即异步发送一次,定时任务只转发超过一定时间仍未发送的消息,作为兜底;已发送的消息定期删除或归档。消息表很大、扫描开销高时,也可以用 Canal 等工具读取 binlog,消息一写入就转发,但同样可能重复发送。 由于重新转发会导致消息被重复投递,积分服务要按订单号加事件类型去重,实现幂等,做法见 [接收端的可靠性](消息队列.md#接收端的可靠性)。订单服务的事务已经提交,无法回滚,积分服务执行失败时只能不断重试或者人工介入处理。所以它适合发券、加积分这类能接受最终一致、失败后可以重试的后续操作。 RocketMQ 的事务消息把本地消息表的工作交给了消息队列:生产者先发送一条对消费者不可见的半消息,再执行本地事务,然后根据执行结果通知 Broker 提交或者回滚这条消息。如果 Broker 长时间没有收到通知,会主动回查生产者的本地事务状态。 +先发半消息,是为了先确认 Broker 可用,并让 Broker 一直持有这条消息:之后生产者无论在哪一步崩溃,Broker 都能通过回查得到结果。回查返回未知时会继续回查,默认最多 15 次,超过后半消息被转入系统主题 TRANS_CHECK_MAX_TIME_TOPIC,不再投递,需要人工处理。 +

### 几种做法的对比 @@ -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 🔥