Redis 分布式锁详解( 三 )

复制代码
tryLock 方法参数说明:
尝试加锁方法 tryLock  , 两个重要的入参 waitTime、leaseTime:

  • waitTime: 尝试加锁的最大时间 , 如果在这个时间内一直没有加锁成功 , 则返回 false 。
  • leaseTime: 锁的有效期 , 如果客户端(进程)在这个时间内没有释放锁 , 则 Redis 主动释放 , 当然 Redisson 看门狗的机制会将这个时间延期 , 后面会说到 。
流程总结:
  1. 尝试加锁 tryAcquire  , 如果加锁成功则返回 null ,  如果锁被占用 , 则返回锁的剩余时间 ttl 。
  2. 如果加锁成功返回 true , 否在判断 waitTime 是否过期 , 过期则加锁失败返回 false 。
  3. 基于信号量 , 通过 Redis 的发布订阅 , 订阅锁的释放事件 , 一旦锁释放会立即通知等待的线程去竞争锁 。
  4. 线程阻塞剩余 waitTime 时间 , 来等待锁释放的通知 , 如果阻塞时间超过了剩余 waitTime 时间 , 则取消任务 , 取消任务成功再取消订阅信息 , 加锁失败返回 false;否则在剩余 waitTime 时间内等到了锁释放通知 , 则进入循环加锁阶段 。
  5. 循环中继续以同样的方式加锁 , 如果在剩余 waitTime 内加锁成功返回 true , 否在加锁失败返回 false 。
  6. 如果在剩余 waitTime 时间内 , 锁还是被其他的客户端(进程)持有 , 阻塞指定时间(持有锁的剩余过期时间和剩余 waitTime 时间)等待锁的释放消息 。
  7. 具体实现:利用信号量(semaphore)阻塞当前线程获取许可 , 如果有可用许可则继续尝试加锁 , 如果没有可用许可则阻塞给定的时间 , 直至其他线程释放锁 , 调用 release() 方法增加许可 , 或者其它某些线程中断当前线程 , 或者已超出指定的等待时间 。
  8. 如果剩余 waitTime 过期 , 加锁失败返回 false 。
加锁<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit, long threadId, RedisStrictCommand<T> command) {internalLockLeaseTime = unit.toMillis(leaseTime);return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,"if (redis.call('exists', KEYS[1]) == 0) then " +// 1、如果 Redis 中不存在这个 key"redis.call('hset', KEYS[1], ARGV[2], 1); " +// 2、设置 key 和 field, 并将 value 的值设置为 1"redis.call('pexpire', KEYS[1], ARGV[1]); " +// 3、设置 key 的过期时间"return nil; " +// 4、返回 null"end; " +"if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +// 5、如果 Redis 中存在对应的 key 和 field"redis.call('hincrby', KEYS[1], ARGV[2], 1); " +// 6、则将对应的 key 和 field 对应的 value 自增 1"redis.call('pexpire', KEYS[1], ARGV[1]); " +// 7、设置 key 的过期时间"return nil; " +// 8、返回 null"end; " +"return redis.call('pttl', KEYS[1]);",// 9、返回剩余生存时间, 单位毫秒// 以下这三个参数分别对应 Lua 脚本中的 KEYS[1], ARGV[1], ARGV[2]Collections.<Object>singletonList(getName()), internalLockLeaseTime, getLockName(threadId));}复制代码
Redisson 中实际加锁的代码 , 流程总结:
  1. 如果 Redis 中不存在 key 。
  2. 则使用 hset 这个命令设置 key 和 field , 并将 hash 的 value 设置为 1 , 这里使用 Redis 中的 hash 数据结构 ,  value 的值用于支持可重入锁 , 记录加锁次数 。
  3. 设置 key 的过期时间 。
  4. 加锁成功返回 null 。
  5. 如果 Redis 中存在对应的 key 和 field 。
  6. 将对应的 key 和 field 对应的 value 值自增 1 , 记录重入锁的次数 。
  7. 设置 key 的过期时间 。
  8. 加锁成功返回 null 。
  9. 加锁失败 , 返回 key 的剩余生存时间(单位毫秒) 。
锁自动续期(Watch Dog 机制)在不指定锁超时时间(leaseTime)的情况下 , Redisson 分布式锁会自动给锁续期 , 也就是所谓的看门狗机制 。
锁自动续期代码解析:


推荐阅读