什么是分布式锁
在分布式系统下,通过锁机制来控制资源的访问,与传统的单体项目中的synchronized,他是基于jvm的锁,即在一个springboot服务下能保证线程同步的问题,但现在我们大部分的项目部署不局限于一台服务器,此时会出现多把jvm锁无法保证对数据的互斥原则。
分布式锁就像是把锁单独抽出来为一个应用,让所有springboot项目公用同一把锁。
分布式锁的特点
1.互斥性:任意时刻锁只能被一个客户端持有
2.安全性:锁只能被持有的客户端删除
3.死锁:获得锁的客户端因为某些原因无法释放锁导致其他客户端一直被阻塞
4.容错:当部分节点宕机时,客户端仍然能获得和释放锁。
分布式锁的实现方式
分布式锁一般有着三种方式:
1.基于数据库的乐观锁
2.基于zookeeper实现的分布式锁
3.基于redis实现的分布式锁
这里我只介绍第三种,也是市面上最常用的一种,其他的如果感兴趣可以看看zookeeper跟数据库乐观锁实现的分布式锁
redisson分布式锁的实现原理
1.通过redis的setnx命令,在key的不存在的时候才会设置对应的value,(redis执行命令时单线程的)这保证了同时只能有一个线程设置成功,
2.然后我们还会设置一个过期时间,防止宕机的时候锁无法释放,通常我们使用ex命令实现的。
但上面方案还存在问题就是过期时间的阈值不好估计,有可能任务还未完成就过期了。redisson通过单独开启一个线程去定期查看任务释放完成,若未完成就会续期嘛,通常是30秒
除了过期时间阈值不好估计,还会存在误释放锁的情况,及我们要手动释放锁的时候,key过期了,此时另外一个线程获取了锁,我们就可能释放了别的线程的锁。针对这种情况,redisson对每个锁都有一个标识,当释放锁的时候判断该锁是否属于自己,然后整个释放锁操作通过lua脚本来保证原子性。整个这个流程就实现了一个分布式锁。但这种分布式锁只适用于单机情况下,当我们进行集群的时候,可能存在多个客户端获取锁违背锁的互斥性。
这种基于redis实现的分布式锁在使用的时候要注意的问题
一.时钟偏移的问题:获得锁的机器系统时间快,会提前释放锁,任务为完全执行导致数据不一致的问题,获得锁的机器系统时间慢,会慢释放锁 导致其他客户端无法获取锁性能下降。
二.集群下多个客户端获得锁:我们知道,在Redis集群中,Master主节点和Slave从节点之间是异步方式进行数据同步的。这样其实就有一个问题,举个例子:
1、客户端A在Master节点拿到了锁;
2、这时候Master需要将锁的信息异步方式同步给Slave从节点,但是这个时候,还没同步完成【把客户端A创建的key写入Slave时】,Master节点挂了,锁没同步成功,这时候Redis会触发Master选举;
3、此时Slave从节点升级成为新的Master主节点,注意此时新的master上面没有客户端A设置的ke值,新master并不知道其实客户端A现在已经获取了锁;
4、其他客户端,如客户端B判断到新Master上面没有那个key,直接set成功,客户端B也获取到和客户端A持有相同的key的锁;
5、这样就违反了分布式锁互斥性了,同一个时刻,两个客户端持有相同的一把锁,想一下,要是这个执行逻辑是扣减库存操作,那么库存是不是可能导致超卖了;