分布式锁具有互斥性锁超时释放防误删高可用高性能的特点。今天我们推荐一个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