redisson实现分布式锁

本文探讨了分布式锁的四个基本条件,比较了基于数据库、分布式协调系统、缓存和Redisson的实现方案,重点介绍了Redisson的加锁、解锁流程,以及watchdog机制和lua脚本的应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

首先,为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

  1. 互斥性。在任意时刻,只有一个客户端能持有锁。
  2. 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。
  3. 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了,即不能误解锁。
  4. 具有容错性。只要大多数Redis节点正常运行,客户端就能够获取和释放锁。

常见分布式锁方案对比

分类方案实现原理优点缺点
基于数据库基于mysql 表唯一索引1.表增加唯一索引
2.加锁:执行insert语句,若报错,则表明加锁失败
3.解锁:执行delete语句
完全利用DB现有能力,实现简单1.锁无超时自动失效机制,有死锁风险
2.不支持锁重入,不支持阻塞等待
3.操作数据库开销大,性能不高
基于分布式协调系统基于ZooKeeper

1.加锁:在/lock目录下创建临时有序节点,判断创建的节点序号是否最小。

若是,则表示获取到锁;否则,则watch /lock目录下序号比自身小的前一个节点
2.解锁:删除节点

1.由zk保障系统高可用
2.Curator框架已原生支持一系列分布式锁命令,使用简单
需单独维护一套zk集群,维保成本高
基于缓存基于redis1. 加锁:执行setnx,若成功再执行expire添加过期时间
2. 解锁:执行delete命令

现简单,相比数据库和分布式系统的实现,

该方案最轻,性能最好

1.setnx和expire分2步执行,非原子操作;

若setnx执行成功,但expire执行失败,就可能出现死锁
2.delete命令存在误删除非当前线程持有的锁的可能
3.不支持阻塞等待、不可重入

基于redis Lua脚本能力1. 加锁:执行SET lock_name random_value EX seconds NX 命令

2. 解锁:执行Lua脚本,释放锁时验证random_value 
-- ARGV[1]为random_value,  KEYS[1]为lock_name

if redis.call("get", KEYS[1]) == ARGV[1] then

    return redis.call("del",KEYS[1])

else

    return 0

end

同上;实现逻辑上也更严谨,除了单点问题,

生产环境采用用这种方案,问题也不大。

不支持锁重入,不支持阻塞等待,不支持锁续期
基于redisson

如下文

1、需要特别注意的是,RedissonLock 同样没有解决 节点挂掉的时候,存在丢失锁的风险的问题。

2、

加锁流程核心就3步 
Step1:尝试获取锁,这一步是通过执行加锁Lua脚本来做; 
Step2:若第一步未获取到锁,则去订阅解锁消息,当获取锁到剩余过期时间后,调用信号量方法阻塞住,直到被唤醒或等待超时 
Step3:一旦持有锁的线程释放了锁,就会广播解锁消息。于是,第二步中的解锁消息的监听器会释放信号量,获取锁被阻塞的那些线程就会被唤醒,并重新尝试获取锁。

watchdog机制:

搞了一个定时调度任务TimerTask,完成锁key过期时间的续约,延迟internalLockLeaseTime/3之后执行,默认internalLockLeaseTime=30000ms,

算下来就是大约10秒钟执行一次,调用了一个异步执行的方法,执行了一段lua脚本,设置锁key的过期时间30000ms

redisson解锁过程:

Q1:广播解锁消息有什么用? 
A:是为了通知其他争抢锁阻塞住的线程,从阻塞中解除,并再次去争抢锁。

加锁&解锁流程:

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值