分布式锁之redis实现

用法

SpringRedisLockV2 lock = SpringRedisLockV2.builder()
        .lockKey(lockKey).waitTime(0L).leaseTime(1L).build();
// 1. 尝试获取锁
boolean isSuccess = Boolean.TRUE.equals(lock.doWithLock(lockContext -> {
    // 2.如果获取锁成功,执行业务处理
    if (lockContext.isSuccess()) {
        ...
    }
    return false;
}));

实现

加锁

// 获取锁失败会跳出等待,例如获取连接失败,操作redis失败场景
protected String tryLock(long waitTime, long leaseTime, TimeUnit unit) throws InterruptedException {
    String result = "FAIL";
    long timeout = unit.toMillis(waitTime);
    long internalLockLeaseTime = unit.toMillis(leaseTime);

    while(timeout >= 0L) {
        result = this.doTryLock(internalLockLeaseTime);
        if ("OK".equalsIgnoreCase(result)) {
            return "OK";
        }

        timeout -= 10L;
        if (timeout > 0L) {
            Thread.sleep(10L);
        }
    }
    return result;
}
protected String doTryLock(long leaseTimeInMillSeconds) {
  List<String> keys = new ArrayList();
  keys.add(this.lockKeys.get(0));
  return (String)this.stringRedisTemplate.execute(SCRIPT_LOCK, keys, new Object[]{this.lockUUID.toString(), String.valueOf(leaseTimeInMillSeconds)});
}
  1. 构建锁,获取锁的等待时间waitTime默认3秒。锁获取后的超时时间leaseTime默认3秒
  2. doWithLock创建锁上下文LockContext
  3. tryLock进入自旋,10毫秒一次重试获取锁。如果获取锁的操作抛出异常会导致等待时间时效,即直接跳出自旋并释放锁unlock
  4. 获取锁成功后将结果设置到上下文中LockContext
  5. finally块执行callback业务逻辑入参为lockContext上下文

解锁

protected long unlock() {
  List<String> keys = new ArrayList();
  keys.add(this.lockKeys.get(0));
  return (Long)this.stringRedisTemplate.execute(SCRIPT_UNLOCK, keys, new Object[]{this.lockUUID.toString()});
}

lua脚本

// 加锁
return redis.call('set', KEYS[1], ARGV[1], 'px', ARGV[2], 'nx')
// 解锁
if (redis.call('exists', KEYS[1]) == 0) then
  return -1;
end;
if (redis.call('get', KEYS[1]) == ARGV[1]) then
  redis.call('del', KEYS[1]);
  return 1;
end
return 0

原理

redis提供的一个原子的命令,中间不会穿插其他指令

redis.call('set', KEYS[1], ARGV[1], 'px', ARGV[2], 'nx')
  1. KEYS[1]:key
  2. ARGV[1]:value
  3. ‘px’:key过期的设置
  4. ARGV[2]:key超时时间
  5. ‘nx’:SET IF NOT EXIST

Q&A

为什么需要value?

如果user1获取锁key1执行业务逻辑中。user2释放了user1的key1对应的锁。
此时user3获取同一个锁key1执行业务逻辑。那么user1与user3,使用的是同一把锁,但是同时在处理业务逻辑,分布式锁是无效的。

为什么需要超时时间?

不设置超时时间,如果业务处理有bug导致死循环就会出现死锁

为什么需要SET IF NOT EXIST?

不使用SET IF NOT EXIST,那么加锁步骤则是:

  1. 获取key,判断key是否存在,并且是否超时
  2. 设置key

如果user1获取key发现不存在,设置key。同一时间user2获取key发现不存在,设置key覆盖了user1的动作

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值