领导权是租约,不是王冠
节点通过获取租约或赢得一轮共识,在某个任期内成为领导者。租约有效期间,它可以执行单例工作。困难的情况是领导者暂停的时间超过租约——例如垃圾回收、虚拟机冻结或网络延迟。另一个节点获得领导权,但暂停的进程恢复后仍然认为自己领导。如果它继续写共享存储,系统就出现了两个领导者。
栅栏令牌可以解决这个问题。每个任期都有一个单调递增的令牌。每次写入都携带该令牌,存储会拒绝令牌小于已见最高值的写入。旧领导者可能仍然认为自己掌权,但它的写入会失败。
任期 7 领导者 A -> 写入(token=7) -> 接受
A 暂停,租约过期
任期 8 领导者 B -> 写入(token=8) -> 接受
A 恢复 -> 写入(token=7) -> 被存储拒绝
选择协调层
etcd、Consul 或 ZooKeeper 等基于共识的存储提供租约和监听通知。关系数据库可以用带过期时间的比较并交换行来实现选举,但应用必须处理时钟偏移和栅栏。不要用长事务中的 SELECT ... FOR UPDATE 来代表整个工作期间的领导权;它会阻塞、超时,并且无法干净地应对进程暂停。
为交接而设计
新领导者应等待旧领导者的租约过期,并等待在途工作稳定后再接管。工作应当幂等或带栅栏,这样重复执行不会破坏状态。优雅停机应主动释放领导权;崩溃则依赖租约过期。监控选举抖动:频繁更换领导者通常意味着网络不稳定、过载,或租约相对于工作耗时过短。
何时使用
当同一时刻只允许一个执行者,且重复执行会造成危害时,使用领导者选举。优点是单写者和清晰的归属模型;缺点是对协调系统的依赖和故障转移延迟。与按 key 分区工作相比,选举更适合全局单例,但更难做到高可用。