在一个运行正常的数据库集群中,主库(Leader)会定期向分布式一致性存储(如 etcd)更新锁(lease)信息,用以维持其主节点的角色。这种锁被称为leader 锁或租约锁(lease lock)。Patroni作为高可用组件,通过其内置的 API Server 定期将主节点的锁信息写入到 etcd 中。
一、锁续约机制
Patroni 的主节点会以周期性的方式向 etcd 发送更新请求(续约),更新成功后会刷新租约的过期时间,继续维持主节点身份。其他候选节点则周期性检查当前锁的状态,判断是否有必要参与选举。
锁续约的流程大致如下:
- 当前主节点向 etcd 写入或刷新锁;
- etcd 响应成功,则主节点保持原状态;
- 若 etcd 响应超时或失败,主节点无法续约,视为锁失效;
- Patroni 检测到锁失效后,主动触发降级(demotion),将当前节点降为只读,从主角色中退出;
- 集群内其余节点通过竞选机制(抢占锁)选举出新的主节点。
二、锁续约失败的常见原因
锁续约失败通常由以下原因引起:
- 网络延迟或抖动:Patroni 与 etcd 通信超时;
- etcd 集群负载过高:导致写入请求阻塞;
- etcd 节点不可用或出现 leader 切换:临时不可写;
- API Server 或 Patroni 自身负载问题:请求未及时发出或被延迟处理。
三、日志中的异常现象分析
根据日志记录,在 x:08:33 - x:08:42
期间发生了 etcd 更新超时。这意味着在此时间段内,部分 Patroni 实例未能及时完成锁的续约。由于整个集群中各节点续约操作的时间并非完全同步,因此:
- 只有在这一时间段内正好发起锁续约请求的节点会受到影响;
- 其余节点由于尚未发起续约操作,可能未受到影响;
- 受影响节点若为主节点,将因无法成功续约而主动降级为只读模式;
- 集群将根据竞选机制选出新的主节点,完成主备切换。
四、优化建议
为了减少类似锁续约失败带来的主备切换影响,可以考虑以下优化方向:
- 调整 Patroni 的续约超时时间和频率,确保在短暂网络波动下仍有机会重试;
- 优化 etcd 集群性能,提升其吞吐能力和响应速度,避免在高负载下出现阻塞;
- 监控 etcd 的请求延迟和 Patroni 锁续约失败率,及时发现潜在问题;
- 避免所有节点同时续约:通过错峰机制降低并发请求压力;
- 设置合理的
ttl
和retry_timeout
:避免因一两次失败而误判主节点状态。