基于setnx命令的分布式锁 1. 加锁 使用 Redis 实现分布式锁,最直接的想法是利用 setnx 和 expire 命令实现加锁。 在 Redis 中,setnx 是「set if not exists」如果不存在,则 setnx 的意思,当一个线程执行
基于setnx命令的分布式锁1. 加锁使用 Redis 实现分布式锁,最直接的想法是利用 setnx 和 expire 命令实现加锁。 在 Redis 中,setnx 是「set if not exists」如果不存在,则 setnx 的意思,当一个线程执行 setnx 返回 1,说明 key 不存在,该线程获得锁;当一个线程执行 setnx 返回 0,说明 key 已经存在,那么获取锁失败。
2. 释放锁释放锁的话,直接通过 DEL 命令删除对应的 key 即可。
为了防止误删到其他的锁,这里我们建议使用 Lua 脚本通过 key 对应的 value(唯一值)来判断。选用 Lua 脚本是为了保证解锁操作的原子性。因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,从而保证了锁释放操作的原子性。
释放锁时,先比较锁对应的 value 值是否相等,value值可以在加锁的时候把当前的线程 ID 当做value,并在删除之前验证 key 对应的 value 是不是自己线程的 ID,避免锁的误释放 3. setnx缺点setnx 的 key 必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放。可以使用expire命令设置锁超时时间 4. 存在问题:setnx 和 expire 不是原子性的操作,假设某个线程执行setnx 命令,成功获得了锁,但是还没来得及执行expire 命令,服务器就挂掉了,这样一来,这把锁就没有设置过期时间了,变成了死锁,别的线程再也没有办 法获得锁了。 基于set命令的分布式锁
使用set命令加锁并设置锁过期时间:
lockKey:加锁的锁名; 不过,这种解决办法同样存在漏洞:如果操作共享资源的时间大于过期时间,就会出现锁提前过期的问题,进而导致分布式锁直接失效。如果锁的超时时间设置过长,又会影响到性能。 为了解决这个问题,我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续期”。在JAVA的Redisson包中有一个”看门狗”机制,已经帮我们实现了这个功能。 redission看门狗分布式锁Redisson 中的分布式锁自带自动续期机制,使用起来非常简单,原理也比较简单,其提供了一个专门用来监控和续期锁的 Watch Dog( 看门狗),如果操作共享资源的线程还未执行完成的话,Watch Dog 会不断地延长锁的过期时间,进而保证锁不会因为超时而被释放。 1.加锁机制
2.watch dog自动延期机制: Redisson在获取锁之后,会维护一个看门狗线程,在每一个锁设置的过期时间的1/3处,如果线程还没执行完任务,则不断延长锁的有效期。刚开始锁的过期时间默认是30秒,可以通过 lockWactchdogTimeout 参数来改变。 每过 10 秒,看门狗就会执行续期操作,将锁的超时时间重置为 30 秒。看门狗续期前也会先判断是否需要执行续期操作,需要才会执行续期,否则取消续期操作。
3.redisson分布式锁的关键点:
Redisson 的分布式可重入锁 RLock 为例来说明如何使用 Redisson 实现分布式锁:
指定锁超时时间,不会使用自动续期机制
只有未指定锁超时时间,才会使用到 Watch Dog 自动续期机制。
如果使用 Redis 来实现分布式锁的话,还是比较推荐直接基于 Redisson 来做的 |
2021-04-08
2021-10-03
2021-07-26
2019-10-11
2022-08-27