用redis构建分布式锁

 

用redis构建分布式锁

 

单实例的实现

从2.6.12版本开始,redis为SET命令增长了一系列选项:html

  • EX seconds – 设置键key的过时时间,单位时秒
  • PX milliseconds – 设置键key的过时时间,单位时毫秒
  • NX – 只有键key不存在的时候才会设置key的值
  • XX – 只有键key存在的时候才会设置key的值

 

若是有2个进程(可能位于不一样机器)须要竞争某个资源,能够为这个资源加锁,锁放在redis里面,这样两个进程都能访问到,例以下面的命令:git

SET resource-name random-value NX EX max-lock-time github

仅当key不存在时,设置一个键值对,而且设置了key的过时时间。redis

若是其中一个进程set成功,那么另一个进程会set失败,只要判断set命令的返回值,就能够判断是否加锁成功。算法

这里resouce-name是须要加锁的资源,而random-value每一个进程均可以写惟一值,而max-lock-time是锁的最大持有时间。安全

 

如何释放锁:服务器

a客户端得到的锁(键key)已经因为过时时间到了被redis服务器删除,可是这个时候a客户端还去执行DEL命令。而b客户端已经在a设置的过时时间以后从新获取了这个一样key的锁,那么a执行DEL就会释放了b客户端加好的锁。网络

if redis.call("get",KEYS[1]) == ARGV[1]
then
    return redis.call("del",KEYS[1])
else
    return 0
end

因为每一个进程写入的value是本身生成的随机数,能够保证一个进程只能删除本身加的锁,而避免误删其它进程加的锁。dom

 

分布式锁

在分布式版本的算法里咱们假设咱们有N个Redis master节点,这些节点都是彻底独立的,咱们不用任何复制或者其余隐含的分布式协调算法。咱们已经描述了如何在单节点环境下安全地获取和释放锁。所以咱们理所固然地应当用这个方法在每一个单节点里来获取和释放锁。在咱们的例子里面咱们把N设成5,这个数字是一个相对比较合理的数值,所以咱们须要在不一样的计算机或者虚拟机上运行5个master节点来保证他们大多数状况下都不会同时宕机。一个客户端须要作以下操做来获取锁:分布式

1.获取当前时间(单位是毫秒)。

2.轮流用相同的key和随机值在N个节点上请求锁,在这一步里,客户端在每一个master上请求锁时,会有一个和总的锁释放时间相比小的多的超时时间。好比若是锁自动释放时间是10s,那每一个节点锁请求的超时时间多是5~50ms的范围,这个能够防止一个客户端在某个宕掉的master节点上阻塞过长时间,若是一个master节点不可用了,咱们应该尽快尝试下一个master节点。

3.客户端计算第二步中获取锁所花的时间,只有当客户端在大多数master节点上成功获取了锁(在这里是3个),并且总共消耗的时间不超过锁释放时间,这个锁就认为是获取成功了。

4.若是锁获取成功了,那如今锁自动释放时间就是最初的锁释放时间减去以前获取锁所消耗的时间。

5.若是锁获取失败了,不论是由于获取成功的锁不超过一半(N/2+1)仍是由于总消耗时间超过了锁释放时间,必定要尽快在获取锁成功的节点上释放锁,这样就不必等到key超时后才能从新获取这个锁(可是若是网络分区的状况发生并且客户端没法链接到Redis节点时,会损失等待key超时这段时间的系统可用性)。

 

注意:当一个客户端获取锁失败时,这个客户端应该在一个随机延时后进行重试,之因此采用随机延时是为了不不一样客户端同时重试致使谁都没法拿到锁的状况出现。一样的道理客户端越快尝试在大多数Redis节点获取锁,出现多个客户端同时竞争锁和重试的时间窗口越小,可能性就越低,因此最完美的状况下,客户端应该用多路传输的方式同时向全部Redis节点发送SET命令。

 

 

 

 

参考文档:

http://ifeve.com/redis-lock/

https://github.com/SPSCommerce/redlock-py

相关文章
相关标签/搜索