持锁实例宕机后系统如何恢复?
简化版
持锁实例宕机后,锁应由租约、临时节点或会话失效自动回收,不能依赖实例执行 finally。新持有者接管前要确认旧主已被隔离,并根据持久化状态恢复未完成业务;资源侧用 Fencing Token 拒绝旧实例复活后的迟到写。
详细版
恢复分三层:锁层等待权威 TTL/会话过期并授予更大代次;业务层从数据库状态、Outbox 或 checkpoint 判断做到哪一步,幂等续做或补偿;资源层原子校验 fencing token。健康检查发现实例失联不等于旧进程已停止,不能由另一个客户端直接强删锁。
RTO 的关键路径包含故障检测/租约到期等待、重新竞选和业务恢复时间;检测与租约倒计时若并行发生,应取实际关键路径而不是机械相加。应监控过期回收、接管次数、恢复积压、最大任务年龄和旧 token 拒绝数,并演练进程 kill、长 GC、网络分区及恢复后旧请求到达。
完整版教学
一、为什么不能指望主动释放
进程崩溃、宿主机断电或网络隔离时,finally 根本无法执行。没有自动失效机制的永久锁会成为死锁;允许其他客户端随意删除又会误伤仍在工作的 owner。锁记录因此必须绑定可由权威系统判断的租约或会话。
记忆钩子:锁能回收只是第一步,业务能从持久事实继续才叫恢复。
二、锁层回收机制
Redis 锁依赖 TTL,到期后 key 消失;ZooKeeper 临时节点在会话确认失效后删除;数据库可用 lease_until 条件抢占。检测必须由锁服务的权威时间与状态决定,客户端心跳只提供续租证据。
| 机制 | 回收触发 | 主要边界 |
|---|---|---|
| TTL | 服务端到期 | 长暂停、时钟语义 |
| 临时节点 | 会话失效 | 会话超时窗口 |
| DB 租约 | 条件更新 | 扫描与数据库可用性 |
三、RTO 怎样估算
若检测完成时租约仍可能剩余 30 秒,随后竞选 2 秒、状态恢复 8 秒、接管验证 3 秒,则从故障发生算最坏约 5+30+2+8+3=48 秒。若 30 秒是从故障时刻开始计算的完整 TTL,它与 5 秒检测并行,等待阶段应按 30 秒而非 35 秒计算,总计约 43 秒。缩短 TTL 可降低 RTO,却提高正常暂停时误回收概率;这是明确取舍而非免费优化。
RTO_lock ≈ wait_until_takeover_allowed + elect + recover + verify
wait_until_takeover_allowed 取决于故障检测与租约失效的实际关键路径
对秒级恢复有要求时,需要更短租约、更稳定续期和可快速加载的 checkpoint,同时仍要防旧主。
四、新持有者如何判断业务进度
锁记录不是业务日志,不能告诉新 owner 上一步是否提交。长任务应周期性写 checkpoint;跨服务流程用状态机和幂等请求;本地修改与 Outbox 在同一事务提交。接管者读取这些事实,从最后确认点继续,而非从头盲目重跑。
例如 100 万条迁移每 1000 条提交游标,宕机最多重做一个批次;每条写以业务键幂等,重复批次不会破坏结果。
五、防止旧实例复活
网络分区或暂停中的旧实例可能在新 owner 接管后恢复。新锁获得更大的 fencing token,数据库或存储只接受大于已见水位的写入;旧 token 被拒绝。仅依赖“旧机器应该已经挂了”无法构成安全证明。
若下游不支持 fencing,应先通过基础设施隔离旧实例的网络/凭证,或把所有写集中到支持代次校验的代理。
六、恢复流程与演练
流程是确认租约失效、授予新代次、读取状态、幂等恢复、小流量验证、清理遗留。自动恢复设置最大重试和人工停机点,避免坏数据被快速扩散。告警要区分锁回收成功但业务恢复失败。
演练同时覆盖 kill -9、节点断网、锁服务切主、长 GC 和旧请求延迟到达,并测实际 RTO、重复处理量及数据差异。
恢复期间应禁止旧部署版本自动拉起后沿用本地缓存的锁句柄。进程启动 epoch、租约重新获取和凭证轮换可以阻断这类“重启后继续旧任务”的路径。
若接管任务会发送用户可见通知,还要把通知纳入幂等范围,避免数据只写一次但短信或邮件重复发送。
七、常见误区与追问
- 误区:实例宕机会执行 finally 释放锁。 非正常退出没有这个保证。
- 误区:发现心跳断开就可以强删锁。 网络分区下旧 owner 可能仍在执行。
- 误区:拿到新锁后从头重跑即可。 已完成副作用可能被重复执行。
- 误区:TTL 到期即可阻止旧主写。 旧进程不会因 key 消失自动停止。
- 追问:RTO 下限由什么决定? 由允许接管前的等待关键路径,再加竞选、恢复和验证时间决定;并行发生的检测与租约倒计时不能重复相加。
- 追问:恢复依据放哪里? 持久化业务状态、checkpoint 和事务 Outbox。
- 追问:如何验证旧主被挡住? 在演练中延迟发送旧 token 写并确认资源拒绝。
八、加强记忆
宕机恢复要串起“租约回收—新代次接管—持久状态续做—旧主栅栏—结果验证”。锁层保证资格最终释放,业务层靠 checkpoint 和幂等收敛,资源层用 fencing 保证迟到写无效。三层缺一,都可能出现锁恢复了但数据仍错误。