使用redis实现分布式锁
Posted 蘑菇骑士团
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用redis实现分布式锁相关的知识,希望对你有一定的参考价值。
在分布式系统中,我们常常会遇到多个进程对共享资源进行互斥访问的场景,使用redis实现分布式锁是一种简单、高效的解决方案。本文简要讨论下使用redis实现分布式锁以及需要注意的点,看看你的实现中是不是也有没考虑到的坑呢?
安全性与活性(Safety & Liveness)
一个正确的分布式锁实现应该同时满足安全性和活性。
安全性:访问互斥,任一时刻只能有一个客户端持有锁。
活性包含两点:
避免死锁。需要保证锁最终一定能被获得,即使某个在客户端释放锁之前down机或者发生网络不可达。
容错性。需要保证只要redis集群中多数节点存活,客户端就能正常获取释放锁。
基于单机redis的分布式锁
我们首先看基于单个redis节点的分布式锁如何实现。相信很多人可以马上按下面的思路写好代码:
setnx创建key,创建成功便获得锁。
访问共享资源,执行业务逻辑。
操作完成,删除key,释放锁。
正常情况下上面的逻辑能够正常执行,但考虑如果客户端A获取锁后挂掉了或由于网络不可达,其他客户端就再不能获取锁了。这时我们可以考虑对key设置一个过期时间,并设置一个唯一值来标识每个客户端,可以使用下面命令来获取锁:
SET resource_name my_random_value NX PX 30000
注意这是一个原子操作,如果分开成 设置key—设置失效时间 两步,如果设置key后客户端挂掉,仍然可能造成死锁。并且只有 value='my_random_value' 才能释放锁,否则可能导致一个客户端释放另一个客户端的锁。对于释放锁,可以使用redis lua脚本:
if redis.call("get",KEYS[1]) == ARGV[1] then
return redis.call("del",KEYS[1])
else
return 0
end
注意del key实际上是一个非原子操作,需要先拿到key、判断、删除。使用lua脚本可以将这个过程转化为原子操作,否则进行到判断 value='my_random_value' 要删除后,由于gc或网络等原因导致删除命令延迟到达,会将其他客户端的锁释放掉。
结合上面的分析,我们总结下单机版redis分布式锁的需要考虑的点:
需要设置锁的失效时间
需要对每个客户端设置唯一的标识
需要保证获取锁、释放锁是原子操作
基于redis集群的分布式锁
单机redis节点如果故障了,服务便不可用了。一般这种情况下,我们会有一个热切换方案,在主节点(Master)上挂一台从节点(Slave),当主节点挂掉了,Slave会升级为主节点。但由于主从节点的数据同步是异步的,可能会导致锁丢失的情况。这种情况下,redis的作者给出了一个叫 Redlock 的算法。假设环境是N个Redis Master节点,这些节点相互独立,无需备份。算法步骤如下:
获取当前时间
依次向每个Master实例尝试获取锁。在获得锁的过程中,为每一个锁操作设置一个快速失败时间,它要远小于锁的有效时间,通过快速失败的方式,尽快尝试与所有节点通信以获取锁
计算整个获取锁的过程消耗了多长时间,如果获取锁的时间小于有效时间,并且半数以上的Redis节点(>= N/2+1)成功获取到了锁,那么认为客户端最终获取锁成功
如果获得锁成功,从新计算锁的有效时间(扣除获取锁的时间)
如果获取锁失败,客户端尝试在所有的master节点中释放锁
那么Redlock是完美的吗?考虑以下序列,假设有5个节点A、B、C、D、E:
客户端1成功锁住了A, B, C,获取锁成功(但D和E没有锁住)。
节点C崩溃重启了,但客户端1在C上加的锁没有持久化下来,丢失了。
节点C重启后,客户端2锁住了C, D, E,获取锁成功。
这样,客户端1和客户端2就都获得了锁,这显然是有问题的。为了解决上面的问题,Redlock又给出了一个延迟重启的概念,保证重启后对现有锁不造成影响。但这样就是安全的了吗?考虑下面这个执行序列:
客户端1在获得锁后,暂停了很久,以至于锁过期并且客户端2也获取了锁。当客户端1恢复后再去操作共享数据,这就打破了数据的安全性。
所以从以上分析上看,设计一个完美的分布式锁是非常困难的,我们需要基于自己的业务场景,选择合适的方案,希望大家在设计分布式锁时能考虑到本文提出的一些注意事项。
参考:
https://redis.io/topics/distlock
以上是关于使用redis实现分布式锁的主要内容,如果未能解决你的问题,请参考以下文章
Redis 作者 Antirez 讲如何实现分布式锁?Redis 实现分布式锁天然的缺陷分析&Redis分布式锁的正确使用姿势!...