Redission 分布式锁原理

Redission 分布式锁原理

Redission 是一redis客户端和jedis、lettuce 一样,但他提供诸多如分布式锁这些方便的工具

加锁过程

Redission 分布式锁原理_第1张图片

lua脚本

"if (redis.call('exists', KEYS[1]) == 0) then 
     redis.call('hset', KEYS[1], ARGV[2], 1); 
     redis.call('pexpire', KEYS[1], ARGV[1]); 
     return nil; 
end; 
if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then 
    redis.call('hincrby', KEYS[1], ARGV[2], 1); 
    redis.call('pexpire', KEYS[1], ARGV[1]); 
    return nil; 
end; 
return redis.call('pttl', KEYS[1]);"

实际上就是分三种情况,做了处理

1、如果锁不存在:使用hest方法创建了锁,filed是线程的唯一标识 value 是重入次数 ,初始为1

2、如果锁存在且为自身持有:使用hincrby 将value加1表示发生了一次重入

3、如果锁存在但非自身持有:返回锁的过期时间(毫秒级别)

watch dog自动延期机制

在使用setnx实现的分布式锁中存在着一个这样的问题:锁无法续期,在实际的业务中可能存在小锁中存在大事务的情况,如果事务的执行时间非常长,此次锁被已经超时释放掉了,就可能存在不可预料的问题产生。redission为我们提供的锁的默认时间是30秒,并且在客户端加锁成功后会启动一个watch dog后台线程,每隔10秒检查一下,如果客户端还持有锁key,那么就会不断的延长锁key的生存时间。

解锁过程

Redission 分布式锁原理_第2张图片

lua脚本

-- 若锁不存在:则直接广播解锁消息,并返回1
if (redis.call('exists', KEYS[1]) == 0) then
    redis.call('publish', KEYS[2], ARGV[1]);
    return 1; 
end;
 
-- 若锁存在,但唯一标识不匹配:则表明锁被其他线程占用,当前线程不允许解锁其他线程持有的锁
if (redis.call('hexists', KEYS[1], ARGV[3]) == 0) then
    return nil;
end; 
 
-- 若锁存在,且唯一标识匹配:则先将锁重入计数减1
local counter = redis.call('hincrby', KEYS[1], ARGV[3], -1); 
if (counter > 0) then 
    -- 锁重入计数减1后还大于0:表明当前线程持有的锁还有重入,不能进行锁删除操作,但可以友好地帮忙设置下过期时期
    redis.call('pexpire', KEYS[1], ARGV[2]); 
    return 0; 
else 
    -- 锁重入计数已为0:间接表明锁已释放了。直接删除掉锁,并广播解锁消息,去唤醒那些争抢过锁但还处于阻塞中的线程
    redis.call('del', KEYS[1]); 
    redis.call('publish', KEYS[2], ARGV[1]); 
    return 1;
end;
 
return nil;

参考博客:

总结

1、redission自带的分布式锁作为附带的工具可以很好的满足我们在分布式系统上的加锁需求

2、相较于setnx 实现的分布式锁,其实现原理更为复杂一些,引入了watch dog机制,实现了锁的可续期,

巧妙的利用hash 结构实现了锁的可重入

3、无论是redission还是setnx实现的分布式锁,在redis的集群模式下,都存在主节点故障导致的问题:

妙的利用hash 结构实现了锁的可重入

3、无论是redission还是setnx实现的分布式锁,在redis的集群模式下,都存在主节点故障导致的问题:

A客户端在主节点写入自己的锁,此时主节点宕机,该数据未及时同步到从节点,而后从节点被选举为主节点,B客户端如果也去写入锁,同样能够成功,此时存在并发写入导致的系列风险问题

你可能感兴趣的:(分布式技术,数据库,java,redis)