分布式锁具有互斥性、锁超时释放、防误删、高可用和高性能的特点。今天我们推荐一个golang版本的基于redis实现的分布式锁:redsync。redsync包是redis官网推荐的golang版本的分布式锁。
下面是根据我自己的理解画的一张redsync设计的简图:
LockUnlockacquire函数release函数
基于redis的setnx,实现分布式锁的互斥性。
acquiresetnx
设置过期时间,防死锁
setnx
在没有给锁设置过期时间的情况下,死锁的产生一般是因为当一个进程A持有锁后,在执行业务逻辑期间,突然崩溃了,那么该进程锁持有的锁就永远无法释放了。
这时,另外一个进程B再获取锁时,因为进程A没有释放锁,所以一直获取不到。那么这个锁就成了死锁或叫做长生锁。
setnxexpire
setnxexpire
value值的随机性+唯一性验证,防误删
setnxvaluevaluegenValueFuncgenValueFuncMutexgenValuegenValuegenValue
所以,value是一个随机值。因为随机性也就产生了唯一性,或者在一定时间范围内是唯一的。其作用就是为了防止被别的进程误删。
被误删的一个前提是锁的有效期到了,锁被自动释放了。以下是一个产生锁被误删的情景。
线程a线程a线程b线程a
rediskeyvalue
redsync中代码的实现如下:
预估业务可执行时间,防获取无效锁
redsyncacuquire
在锁的生命周期内其实是有 获取锁的时间+漂移时间+业务执行时间三部分组成的。
那么留给业务的执行时间就是:过期时间 - 获取锁的时间 - redis服务器漂移时间
再用 当前时间 + 留给业务的时间 就能推导出业务执行的截止时间。 如果当前时间已经超过了业务运行的截止时间,那么就说明锁已经过期了(比如获取锁的时间过长),就需要释放锁,并返回加锁失败。
重试机制,提高获取锁的效率
redsync
那为什么需要重试机制呢?
首先重试增加获取锁的稳定性。在分布式系统中,由于网络延迟等原因,获取锁的操作可能会失败。等待一段时间后再进行重试可以增加系统的稳定性,从而降低系统崩溃的概率。
其次,要防止频繁重试。如果在获取锁时发生错误,立即进行重试可能导致系统频繁重试,从而导致性能下降。因此,在等待一段时间后再进行重试可以减少这种情况的发生。
多redis节点支持,保证高可用性
redsyncredis
m.poolsredisMutexquorum