在redis文档中,我发现可以通过SETNX实现原始锁:
http://redis.io/commands/setnx
C4发送SETNX lock.foo以获取锁
崩溃的客户端C3仍然拥有它,因此Redis将回复0到C4。
C4发送GET lock.foo以检查锁是否过期。如果不是,它会睡一段时间并从头开始重试。
相反,如果锁定已过期,因为lock.foo上的Unix时间早于当前的Unix时间,C4会尝试执行:
GETSET lock.foo
由于GETSET语义,C4可以检查存储在key中的旧值是否仍然是过期时间戳。如果是,则获得锁定。
如果另一个客户端(例如C5)比C4快并且通过GETSET操作获得锁定,则C4 GETSET操作将返回未过期的时间戳。 C4将从第一步开始重新启动。请注意,即使C4将来设置密钥几秒钟,这也不是问题。
但是,正如一些用户评论的那样,使用UNIX时间戳作为过期时间需要客户端和服务器的时间完全同步。有没有更好的替代方法在Redis中创建全局/分布式锁?
答案 0 :(得分:12)
使用SET
代替SETNX
。 SET
接受过期时间的参数,以秒和毫秒为单位,而不是UNIX时间戳值。
仅基于历史原因记录旧的基于SETNX的模式。
来自SETNX
description:
注意:从Redis 2.6.12开始,可以创建更多 使用SET命令获取锁定更简单的锁定原语, 和一个简单的Lua脚本来释放锁。该模式已记录在案 在SET命令页面中。
答案 1 :(得分:8)
使用 redis> = 2.6 LUA脚本解决方案会很棒。 Lua脚本总是以原子方式执行,所以:
--lockscript, parameters: lock_key, lock_timeout
local lock = redis.call('get', KEYS[1])
if not lock then
return redis.call('setex', KEYS[1], ARGV[1], "locked");
end
return false
另一种基于SET
命令
SET lock_key "locked" EX lock_timeout NX
使用 redis< 2.6 可以使用带有multi的模式:
MULTI
SETNX tmp_unique_lock some_value
EXPIRE tmp_unique_lock
RENAMENX tmp_unique_lock real_lock
EXEC
答案 2 :(得分:3)
答案 3 :(得分:1)
SET的新参数足以设置锁定,但这些仅适用于Redis> = v2.6.12,您还需要考虑锁定将如何取消设置并过期等。
我在工程博客上写了一篇关于distributed locks using Redis的帖子。它包含有关如何可靠地设置和释放锁的脚本,以及防止验证和死锁。我还包括一个用Node.js编写的模块,你可以直接用于锁定。
答案 4 :(得分:1)