一、为什么要用Redisson的分布式锁?
- JAVA自带的Synchronized\Lock等锁,都是JVM控制的锁,属于单机锁。可用于单机并发的场景,但是在分布式高并发场景下,单机锁无法锁住资源。
- 使用Redis的setnx命令的分布式锁,有以下缺点:
- 是不可重入的,同一个线程无法多次获取同一把锁。
- 不可重试,获取锁只尝试一次就返回false,没有重试机制。
- 锁超时释放,虽然可以避免死锁,但如果是业务执行耗时较长,也会导致锁释放,存在安全隐患。
二、Redisson分布式锁的执行流程
- 线程A尝试加锁,加锁成功后,会开启一个新线程充当看门狗,默认每10s执行一次续期操作。默认的过期时间是30s。
- 此时线程B也来尝试加锁,加锁失败后,会一直while循环尝试获取锁。到达阈值waitTime后不再尝试加锁。这就是分布式锁的重试机制。
加入重试机制的好处是:在高并发情况下,可以最大限度的提高锁的使用频率。
三、Redisson分布式锁的原理
3.1 加锁
3.1.1 Redisson锁是可重入锁
-
可重入:一个线程在持有锁的请求下,可以再次请求锁而不被阻塞。
-
为什么Redisson分布式锁是可重入的?
因为锁是利用hash结构记录线程ID和重入次数的。
| key | value | vlaue |
|---|---|---|
| filed | value | |
| lockname | threadID | 锁重入次数 |
3.1.2 Redisson锁是可以失败重试的
3.1.3 Redisson锁的自动续期--看门狗原理
3.2 释放锁
三、分布式锁的特性--主从一致性
1. 为什么会发生主从数据不一致?
- 线程1 在redis master节点获取锁lock成功。
- 在master节点向slave节点同步锁lock数据之前,master发生了宕机。
- 哨兵模式从2个slave节点中选举出一个节点作为master节点。
- 线程2 在新的master节点获取锁lock成功。
- 此时就有两个不同的线程同时持有同一把锁,破坏了锁的互斥性。
目前有两种方法解决主从不一致问题:redis的RedLock和zookepper的分布式锁。
2. 使用redis的分布式锁RedlocK解决主从数据不一致问题。--不推荐
- RedLock(红锁):不能只在一个redis实例上创建锁,应该是在多个(n/2+1)redis实例上创建锁,避免在一个redis实例上加锁。
- RedLock在实际项目中很少用到,因为它的缺点很多。
- 使用RedLock之后,实现变复杂。
- 需要提供多个独立的redis节点,在高并发情况下,性能会变得很差。
- 运维成本高,维护繁杂。
redis出现节点宕机的情况是极低的,而且redis集群的思想是AP(Availability, Partition tolerance)思想,优先保证高可用性,可以想办法做到最终一致性。如果一定要保证主从一致性,推荐使用zookeeper,它是CP(Consistency, Partition tolerance)思想。
3. 使用zookeeper的分布式锁解决主从数据不一致问题。--推荐
四、代码演示
加锁、设置过期时间等操作都是在一个LUA脚本中完成的,保证了多条命令执行的原子性。