redis分布式锁

加锁

  1. 生成一个特殊值(比如随机值+当前线程ID),记录在在ThreadLocal里。
  2. 通过setnx向特定的key写入第一步生成的随机值,同时设置失效时间,操作成功则代表加锁成功。
SET resource_name my_random_value NX PX 30000

说明

  • 设置一个失效时间是为了避免死锁。
  • 写入一个随机值是为了避免加锁与解锁是同一线程
  • 写入随机值与设置失效时间是同时的是为了保证加锁是原子操作。

解锁

根据key取得随机值,从ThreadLocal里取出随机数,进行二者判断,删除redis上的key数据,要保证获取数据、判断随机值是否匹配及删除数据这三个操作是原子的,可通过lua脚本实现。

if redis.call("get",KEYS[1]) == ARGV[1] then
     return redis.call("del",KEYS[1])
else
     return 0
end

潜在的问题:过期时间设多长?

设置过小,有可能上一个线程还没执行完锁的逻辑锁就释放了,另一个线程获取锁然后出现并发问题。
设置过大,如果客户端断线了,这个锁要等待很长时间。
这个问题Redisson提供了自动延期机制也就是watch dog

Redisson中客户端一旦加锁成功,如果客户端没有主动设置失效时间就会启动一个watch dog看门狗。watch dog是一个后台线程,会每隔10s(可配置)检查一下,如果客户端还持有锁key,那么就会不断的延长锁key的生存时间。
如果客户端出现了宕机,那客户端加的锁会怎么样呢?首先因为续锁线程(一个定时任务)和加锁线程同在一个JVM实例里,机器宕机后续锁线程也会挂掉所以不会出现一直续期的情形。另外在在客户端没有设置失效时间,Redisson会有一个默认的过期时间30s(也可以通过Config.lockWatchdogTimeout配置进行调整),因为续期线程已不存在,所以到了时间后自然会删除锁,这样就不会存在死锁的情形。
另外需要说明下,线程被中断或在续期过程中设置过期时间失败都会释放锁。

redis分布式锁_第1张图片

这个方案是目前最优的分布式锁方案,但是如果在Redis集群环境下依然存在问题:

由于Redis集群数据同步为异步,假设在Master节点获取到锁后未完成数据同步情况下Master节点crash,此时在新的Master节点依然可以获取锁,所以多个Client同时获取到了锁。针对这个问题,出现了Redlock解决方案。

参考的文档:
RedissonBaseLock源码
Redisson分布式锁源码分析
Redlock的说明
Redlock源码
Redlock源码分析
Redisson锁续期定制化调整
Redisson重连后WatchDog失效问题解决
Redisson说明文档

你可能感兴趣的:(redis)