这篇文章主要是对 Redis
官方网站刊登的 Distributed locks with Redis 部份内容的总结和翻译。程序员
Redis
官方站这篇文章提出了一种权威的基于 Redis
实现分布式锁的方式名叫 Redlock,此种方式比原先的单节点的方法更安全。它能够保证如下特性:redis
client
能拿到锁client
均可能拿到锁,不会出现死锁的状况,即便本来锁住某资源的 client crash
了或者出现了网络分区Redis
节点存活就能够正常提供服务SET resource_name my_random_value NX PX 30000算法
主要依靠上述命令,该命令仅当 Key
不存在时(NX
保证)set
值,而且设置过时时间 3000ms
(PX
保证),值 my_random_value
必须是全部 client
和全部锁请求发生期间惟一的,释放锁的逻辑是:安全
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
复制代码
上述实现能够避免释放另外一个 client
建立的锁,若是只有 del
命令的话,那么若是 client1
拿到 lock1
以后由于某些操做阻塞了很长时间,此时 Redis
端 lock1
已通过期了而且已经被从新分配给了 client2
,那么 client1
此时再去释放这把锁就会形成 client2
本来获取到的锁被 client1
无端释放了,但如今为每一个 client
分配一个 unique
的 string
值能够避免这个问题。至于如何去生成这个 unique string
,方法不少随意选择一种就好了。网络
算法很易懂,起 5
个 master
节点,分布在不一样的机房尽可能保证可用性。为了得到锁,client
会进行以下操做:dom
ms
)单位5
个实例上申请锁,固然须要使用相同的 key
和 random value
,这里一个 client
须要合理设置与 master
节点沟通的 timeout
大小,避免长时间和一个 fail
了的节点浪费时间client
在大于等于 3
个 master
上成功申请到锁的时候,且它会计算申请锁消耗了多少时间,这部分消耗的时间采用得到锁的当下时间减去第一步得到的时间戳获得,若是锁的持续时长(lock validity time
)比流逝的时间多的话,那么锁就真正获取到了。lock validity time
应该是 origin(lock validity time) - 申请锁期间流逝的时间client
申请锁失败了,那么它就会在少部分申请成功锁的 master
节点上执行释放锁的操做,重置状态若是一个 client
申请锁失败了,那么它须要稍等一会在重试避免多个 client
同时申请锁的状况,最好的状况是一个 client
须要几乎同时向 5
个 master
发起锁申请。另外就是若是 client
申请锁失败了它须要尽快在它曾经申请到锁的 master
上执行 unlock
操做,便于其余 client
得到这把锁,避免这些锁过时形成的时间浪费,固然若是这时候网络分区使得 client
没法联系上这些 master
,那么这种浪费就是不得不付出的代价了。分布式
放锁操做很简单,就是依次释放全部节点上的锁就好了性能
若是咱们的节点没有持久化机制,client
从 5
个 master
中的 3
个处得到了锁,而后其中一个重启了,这时注意:网站
整个环境中又出现了 3 个 master 可供另外一个 client 申请同一把锁!lua
这违反了互斥性。
若是咱们开启了 AOF
持久化那么状况会稍微好转一些,由于 Redis
的过时机制是语义层面实现的,因此在 server
挂了的时候时间依旧在流逝,重启以后锁状态不会受到污染。可是考虑断电以后呢,AOF
部分命令没来得及刷回磁盘直接丢失了,除非咱们配置刷回策略为 fsnyc = always
,但这会损伤性能。解决这个问题的方法是,当一个节点重启以后,咱们规定在max TTL
期间它是不可用的,这样它就不会干扰本来已经申请到的锁,等到它 crash
前的那部分锁都过时了,环境不存在历史锁了,那么再把这个节点加进来正常工做。
这是一个不定时更新的、披着程序员外衣的文青小号。既分享极客技术,也记录人间烟火。