标准答案

  1. 每次获取锁生成全局唯一、不可预测的 token,并在同一个原子命令中写入 token 与 TTL,例如 SET key token NX PX leaseMs。
  2. 业务完成后用 Lua 脚本先比较当前值是否等于 token,匹配才删除;比较和删除必须原子完成,不能先 GET 再 DEL。
  3. TTL 要覆盖正常临界区并预留尾延迟,但不能无限长;超时后需要让后续请求可恢复,同时让原持有者的后续写入能够被下游识别为过期。
  4. 续租只应由仍在正常执行且 token 匹配的持有者发起,并限制最长总租约,避免失控任务长期占锁。

题目解析

直接 DEL 的竞态是:A 的锁过期,B 获得新锁,A 恢复后执行 DEL,B 的锁被误删。随机 token 和原子比较删除解决的是这个最基本的所有权问题。

所有权正确并不代表业务正确。A 在锁过期后仍可能继续向数据库写旧结果,因此关键资源还需要版本、序号或 fencing token 做最终校验。

代码示例

释放操作必须把比较与删除放在同一段原子脚本中。

Lua
if redis.call('GET', KEYS[1]) == ARGV[1] then
  return redis.call('DEL', KEYS[1])
end
return 0

常见误区

  • 用用户 ID、机器名或固定字符串作为锁值,多个重入请求无法区分。
  • 获取成功后没有 TTL,或续租无限制延长。

作者信息