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