背景
在我们的项目中,有一个需求是定期进行过期任务的清理。由于后台管理采用了多实例,可能会出现多个实例竞争去清理的情况。
分布式锁需要满足的几个条件
1.系统是一个分布式系统(关键是分布式,单机的可以使用ReentrantLock或者synchronized代码块来实现)
2.共享资源(各个系统访问同一个资源,资源的载体可能是传统关系型数据库或者NoSQL)
3.同步访问(即有很多个进程同事访问同一个共享资源。没有同步访问,谁管你资源竞争不竞争)
本项目采用了多台tomcat服务器+redis【多台tomcat服务器访问一台redis】+mysql【多台tomcat服务器访问一台服务器上的mysql】的架构,满足使用分布式锁的条件
分布式锁,可以有很多方式实现,比如redis、zookeeper。其核心是用一个状态值表示锁,对锁的占用和释放通过状态值来表示
分析之后,由于我们用了redis,可以用redis分布锁。
redis的几个命令
setnx 先判断键是否存在,key存在设置失败,返回0;否则成功返回1
getset 获取旧的值,设置新的值
del 删除键
(1)方式1,会产生死锁
image.png
逻辑:setnx存入key=锁的名字,value=当前时间+超时时间,如果返回1 ,表示成功获得锁,则执行业务逻辑,最后删除锁,如果返回1,则锁已经被占用,表示已经有客户机处理业务逻辑,则直接结束。
缺点:当开了client1和client2,如果client1执行完setnx,进行业务逻辑时,突然重启了,此时redis里的lockkey还被锁着,则另一个客户机永远都无法获得锁,也就无法执行业务逻辑,发生了死锁现象。
(2)方式2,防死锁
image.png
区别之处在于获取锁失败时,根据get获得锁的值expireTime,将其跟当前时间比较,若判定锁已经超时,则表示已经获得锁的客户机有可能已经崩溃,因此现在的客户机有权利获得锁,用
getset方法获得oldvalue,并且设置新的过期时间,如果oldvalue=expireTime,则表示没有其他客户机抢先获得了锁,因此获得锁成功,执行业务逻辑,否则结束。









网友评论