加锁
- 生成一个非凡值(比方随机值 + 以后线程 ID),记录在在 ThreadLocal 里。
- 通过 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 集群环境下仍然存在问题:
因为 Redis 集群数据同步为异步,假如在 Master 节点获取到锁后未实现数据同步状况下 Master 节点 crash,此时在新的 Master 节点仍然能够获取锁,所以多个 Client 同时获取到了锁。针对这个问题,之前提出了 Redlock 解决方案,不过当初已被官网弃用了,起因是 Redisson RedLock 是基于联锁 MultiLock 实现的,然而应用过程中须要本人判断 key 落在哪个节点上,对使用者不是很敌对。这个是被弃用的阐明:Redlock 弃用起因,上面是英文文档:
https://github.com/redisson/r…
8.4. RedLock
This object is deprecated. RLock operations now propagated to all Redis slaves.
参考的文档:
RedissonBaseLock 源码
Redisson 分布式锁源码剖析
Redlock 的阐明
Redlock 源码
Redlock 源码剖析
Redisson 锁续期定制化调整
Redisson 重连后 WatchDog 生效问题解决
Redisson 阐明文档