分布式锁
为什么需要分布式锁
众所周知,锁是高并发下的重要概念,利用锁能够保证数据的一致性。在单机模式下,JDK为我们提供了很多锁,并且足够使用了,这里我们不予讨论。而如果实在分布式环境下,不同的服务可能运行在不同的服务器上,运行的实例不同,那JDK的锁就失效了。
- JDK锁单机运行情况
-
JDK锁分布式运行情况(锁失去了互斥性)
我们假设微服务A先取得资源A,但由于JDK的锁是基于JVM运行的,只能锁住单个JVM进程中的资源A,其他微服务是无法锁住,因此微服务B也取得了资源A。两个微服务的资源A值是相等的,但运行于不同JVM上。
假设此时资源A的值为1,微服务A运行逻辑将其自增1为2,并写入了数据库;微服务B运行逻辑也自增1,也写入数据库。
这时你会发现明明进行了两次自增,但资源A却仍然为2,此时就需要分布式锁了。
实现分布式锁
JDK锁在分布式环境下失效是由于多个微服务之间锁住的是不同资源,那么我们只要让他们锁住同一资源即可。
使用redis,服务A访问资源A时,执行命令setnx lockA lock
,微服务B想要访问资源B,也会去执行setnx lockA lock
,上锁失败无法访问资源A,就保证了互斥性。只有服务A完成业务,并解锁释放资源(也就是执行del lockA
),服务B才能上锁访问资源。
但这时就会有新的问题出现,如果服务A在锁住了资源之后,突然宕机,没有执行解锁,就会造成死锁。解决也很简单,给锁指定失效时间就行(set lockA lock nx ex 3
)。
如何合理设置分布式锁的失效时间
这时新的问题有出现了,如果我们的业务没执行完,锁就失效了,这样就出问题了呀,有以下两种解决方案
-
合理评估业务需要执行多长时间
-
Redisson的看门狗机制
如果在执行redisson的tryLock方法时,不指定失效时间或传入-1,就会启用看门狗机制。看门狗机制会在默认超时时间30s之后,如果业务还没有执行完,会自动续期
30/3=10s
;而如果业务执行出现异常,看门狗就不会自动续期,而是释放锁
redisson锁的可重入性
我们知道JDK的锁在同一线程内可以重复上锁就叫做可重入锁,redisson同样也可以,实现原理也很类似。
redisson锁会保存当前线程的线程ID和重入次数,当发现执行tryLock方法的线程是同一线程时,就会自增1,解锁就会自减1,这样就保证了可重入性。
redisson锁的主从一致性
起因
有时候为了提高性能和可用性,会部署redis集群,如果使用的是redis主从模式,主节点负责写,从节点负责读,那么就可能会出现在redis主节点锁住资源A,接着宕机了,从节点还没同步主节点的锁,就被选举上位,就能够再次对资源A再次上锁,违反了锁的互斥性。
如何解决
redis官方提供了red lock,当超过半数的节点同步了主节点信息,才被认为是正常的数据。具体可以自行了解,这种方法性能较差,一般不使用。