How to create a distributed lock with Redis?

﹥>﹥吖頭↗ 提交于 2019-12-18 11:37:03

问题


On the redis documentation, I found a primitive lock can be implemented via SETNX:

http://redis.io/commands/setnx

  • C4 sends SETNX lock.foo in order to acquire the lock

  • The crashed client C3 still holds it, so Redis will reply with 0 to C4.

  • C4 sends GET lock.foo to check if the lock expired. If it is not, it will sleep for some time and retry from the start.

  • Instead, if the lock is expired because the Unix time at lock.foo is older than the current Unix time, C4 tries to perform:

    GETSET lock.foo

  • Because of the GETSET semantic, C4 can check if the old value stored at key is still an expired timestamp. If it is, the lock was acquired.

  • If another client, for instance C5, was faster than C4 and acquired the lock with the GETSET operation, the C4 GETSET operation will return a non expired timestamp. C4 will simply restart from the first step. Note that even if C4 set the key a bit a few seconds in the future this is not a problem.

However, as some users commented, using a UNIX timestamp as the expiration requires the client 's and server's time to be perfectly synchronized. Is there a better alternative to create a global/distributed lock in Redis?


回答1:


Use SET instead of SETNX. SET accepts arguments for expiration time in seconds and milliseconds instead of UNIX timestamp value.

The old SETNX based pattern is documented only for historical reasons.

From SETNX description:

NOTE: Starting with Redis 2.6.12 it is possible to create a much simpler locking primitive using the SET command to acquire the lock, and a simple Lua script to release the lock. The pattern is documented in the SET command page.




回答2:


Using redis >= 2.6 the LUA script solution would be great. Lua script always executed atomically so:

--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

The another solution based on new options of SET command

SET lock_key "locked" EX lock_timeout NX 

Using redis < 2.6 the pattern with multi can be used:

MULTI
SETNX tmp_unique_lock some_value
EXPIRE tmp_unique_lock
RENAMENX tmp_unique_lock real_lock
EXEC



回答3:


follow the link
nice project explaining locking
http://redis.io/topics/distlock
https://github.com/mrniko/redisson




回答4:


The new arguments for SET are enough for setting the lock, but these only work on Redis >= v2.6.12 you also need to think about how the lock will be unset and expire etc.

I've written a post on our Engineering blog about distributed locks using Redis. It covers scripting on how to set and release the lock reliably, with validation and deadlock prevention. I also include a module written in Node.js you can use for locking straight out of the box.




回答5:


I gem'ed out the SET EX NX solution that misterion mentioned to a cool gem - simple_redis_lock

The code is simple and looks like this:

def lock(key, timeout)
  if @redis.set(key, Time.now, nx: true, px: timeout)
    begin
      yield
    ensure
      release key
    end
  end
end


来源:https://stackoverflow.com/questions/20736102/how-to-create-a-distributed-lock-with-redis

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!