如何使用Redis创建分布式锁?

时间:2013-12-23 02:01:49

标签: redis locking

在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中创建全局/分布式锁?

5 个答案:

答案 0 :(得分:12)

使用SET代替SETNXSET接受过期时间的参数,以秒和毫秒为单位,而不是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)

点击链接
很好的项目解释锁定
http://redis.io/topics/distlock
https://github.com/mrniko/redisson

答案 3 :(得分:1)

SET的新参数足以设置锁定,但这些仅适用于Redis> = v2.6.12,您还需要考虑锁定将如何取消设置并过期等。

我在工程博客上写了一篇关于distributed locks using Redis的帖子。它包含有关如何可靠地设置和释放锁的脚本,以及防止验证和死锁。我还包括一个用Node.js编写的模块,你可以直接用于锁定。

答案 4 :(得分:1)

我发现了一个很酷的宝石所提到的c:\ProgramsX86\...解决方案 - simple_redis_lock

代码很简单,如下所示:

SET EX NX