分布式锁
在分布式系统下,不同的服务或者客户端都运行在各自的JVM进程下,多个服务共享同一份资源的话,那么就出问题了,本地的锁就没用了在这种情况之下。
分布式锁要保证有以下几点功能:
1.互斥性:任意时刻,只有一个线程能够拿到锁。
2.可重入:一个人拿到锁之后以后还可以再次的拿到锁。
3.高可用:如果哪个服务的释放锁的逻辑出现问题,就可能会导致锁没有释放。高可用就是保证了即使出现问题锁也要正常的释放。
说说分布式锁中的Redis分布式锁吧
Redis 分布式锁实现是用setnx这个,setnx是set if not exixts,set key value
,如果key不存在就设置,存在就什么也不做。
释放锁直接del key
就释放锁了。
为了以防万一误删除其他的锁,可以使用lua脚本,它通过key对应的value来判断,lua脚本是原子性操作。
这样有什么问题没有?
答案是有的。如果在释放锁的时候服务挂掉了,那么锁就没有释放,其他服务的线程无法访问共享资源。—很大的一个问题。
解决这个问题可以在执行setnx命令的时候加上一个过期时间ex,到时间释放锁。
SET lockKey uniqueValue EX 3 NX
ex 过期时间
nx 如果当lockKey不存在的时候才能设置成功。
想一想,这样做还有什么问题没有?
答案依然是有的。
好像加上了一个过期数值解决了上述问题,根据设置过期时间数值的大小可以分为以下两种情况:
1.过期时间大于对共享资源操作的时间----->会影响性能,可以说占着茅坑不拉*。
2.过期时间小于对共享资源操作的时间----->导致分布式锁直接就失效了。
这怎么办呢?
可以用Redission,Redisson 是一个开源的 Java 语言 Redis 客户端,提供了很多开箱即用的功能,不仅仅包括多种分布式锁的实现。并且,Redisson 还支持 Redis 单机、Redis Sentinel 、Redis Cluster 等多种部署架构。
这其中有一个Watch Dog,看门狗,它的作用就是用来检测过期时间的,如果操作的线程还没有执行完的话,会不断的续期,进而保证锁不会因为执行超时被释放掉。
Watch Dog续费的原理就是上述提到的lua脚本,保证其原子性,默认是30秒的超市时间。
Redis 如何解决集群情况下分布式锁的可靠性?
在Redis集群下,由于Redis集群同步数据到各个结点是异步的,如果主节点刚获取到锁,他就挂了,因为主从之间有延迟,还没有同步到从节点, 此时新的 Redis 主节点依然可以获取锁,所以多个应用服务就可以同时获取到锁。
针对这个问题,Redis 之父 antirez 设计了 RedLock (红锁)来解决。
该算法的思想是客户端向Redis集群中的多个实例一次发送加锁的请求,在由半数以上的实例完成加锁的操作,就认为加锁成功,否则就加锁失败。
文章参考:https://javaguide.cn/distributed-system/distributed-lock