Fencing Token:分布式锁过期后,怎样挡住迟到的旧写入?
TL;DR
Fencing Token 用单调递增的代次号让存储端拒绝过期持有者的迟到写入。本文解释为什么必须由受保护资源校验令牌。
Fencing Token 用单调递增的代次号让存储端拒绝过期持有者的迟到写入。本文解释为什么必须由受保护资源校验令牌。

分布式系统中,拿到锁并不意味着你永远拥有它。客户端可能因网络暂停、长时间 GC 或机器休眠错过租约期限;锁服务随后把资源交给另一个客户端。最危险的情况是:旧客户端恢复运行,以为自己仍持有锁,继续向存储系统写入,把新持有者刚写的数据覆盖掉。Fencing Token(栅栏令牌)就是为迟到的旧操作设置一道屏障。
做法通常是在每次成功获得锁时分配一个单调递增的代次号,例如 41、42、43。持有者每次向受保护资源写入时都附上自己的 token。资源端记录目前见过的最大 token,只接受不小于当前有效代次的操作。若旧持有者拿着 41 迟到,而新持有者已经用 42 写过数据,资源端就能拒绝这笔旧写入。
关键点是“由资源端执行检查”。如果 token 只在锁服务里递增,却没有被存储系统、数据库或写入目标检查,迟到者仍可能直接覆盖结果。这个方案把锁所有权的时间变化转成资源可以验证的顺序关系。Chubby 论文中的 sequencer 机制就是相关设计背景之一:应用把 sequencer 附在操作上,服务端能够验证当前锁代次。
栅栏令牌也有工程前提:token 必须在所有持有者之间单调增长且不会意外复用;资源端应可靠持久地保存最大值;并发操作如何比较、重试及处理批量写入,也要定义清楚。若资源跨多个独立系统,所有相关写入面都需要遵守同一规则。token 不能自动让锁服务、数据库和缓存保持一致。
它和普通租约锁保护的侧重点不同。租约帮助协调“谁此刻应当工作”,fencing 则让资源拒绝已经过时的写入,即使旧客户端不知自己过期。两者结合更能抵抗暂停与网络分区造成的迟到请求;如果写入目标无法校验令牌,就不能假设单靠锁租约已彻底消除风险。
理解 fencing token 时,可以记住一句话:旧持有者不需要知道自己已经过期,真正受保护的资源也能识别并拒绝它。这正是分布式系统中把“过期”变成可执行安全规则的关键。
来源:Burrows,The Chubby Lock Service for Loosely-Coupled Distributed Systems



