美文网首页
redis实现分布式锁

redis实现分布式锁

作者: shark没有辣椒 | 来源:发表于2021-11-20 11:34 被阅读0次

今天我们尝试通过单节点Redis实现一个简单的分布式锁。

1.加锁

加锁实际上就是在redis中,给Key键设置一个值,为避免死锁,并给定一个过期时间。
SET lock_key random_value NX PX 5000

  • random_value 是客户端生成的唯一的字符串。
  • NX 代表只在键不存在时,才对键进行设置操作。
  • PX 5000 设置键的过期时间为5000毫秒。

这样,如果上面的命令执行成功,则证明客户端获取到了锁。

2.解锁

解锁就是将key键删除,但也不能乱删,不能说客户端1的请求将客户端2的锁给删除掉。这时候random_value的作用就体现出来。
为了保证解锁操作的原子性,我们用LUA脚本完成这一操作。先判断当前锁的字符串是否与传入的值相等,是的话就删除Key,解锁成功。

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

至此,单节点Redis的分布式锁的实现就已经完成了。比较简单,但是问题也比较大,最重要的一点是,锁不具有可重入性。

这个问题在redisson中得到了解决,redisson 是基于redis的扩展库,使得redis除了应用于缓存以外,还能做队列等数据结构,直接使用的分布式锁,以及人物调度器等。

redisson获取锁执行的是tryLockInnerAsync方法,它是一段LUA脚本代码。在这里,它使用的是hash数据结构。

<T> RFuture<T> tryLockInnerAsync(long leaseTime, TimeUnit unit,     
                            long threadId, RedisStrictCommand<T> command) {

        //过期时间
        internalLockLeaseTime = unit.toMillis(leaseTime);

        return commandExecutor.evalWriteAsync(getName(), LongCodec.INSTANCE, command,
                  //如果锁不存在,则通过hset设置它的值,并设置过期时间
                  "if (redis.call('exists', KEYS[1]) == 0) then " +
                      "redis.call('hset', KEYS[1], ARGV[2], 1); " +
                      "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                      "return nil; " +
                  "end; " +
                  //如果锁已存在,并且锁的是当前线程,则通过hincrby给数值递增1
                  "if (redis.call('hexists', KEYS[1], ARGV[2]) == 1) then " +
                      "redis.call('hincrby', KEYS[1], ARGV[2], 1); " +
                      "redis.call('pexpire', KEYS[1], ARGV[1]); " +
                      "return nil; " +
                  "end; " +
                  //如果锁已存在,但并非本线程,则返回过期时间ttl
                  "return redis.call('pttl', KEYS[1]);",
        Collections.<Object>singletonList(getName()), 
                internalLockLeaseTime, getLockName(threadId));
    }

这里我们可以看出三点:

  • 通过exists判断,如果锁不存在,则设置值和过期时间,加锁成功
  • 通过hexists判断,如果锁已存在,并且锁的是当前线程,则证明是重入锁,加锁成功
  • 如果锁已存在,但锁的不是当前线程,则证明有其他线程持有锁。返回当前锁的过期时间,加锁失败

加锁成功后,在redis的内存数据中,就有一条hash结构的数据。Key为锁的名称;field为随机字符串+线程ID;值为1。如果同一线程多次调用lock方法,值递增1。
解锁方法就不再赘述了,感兴趣可以看看下面的原文链接。


本文参考自https://www.jianshu.com/p/47fd7f86c848

相关文章

网友评论

      本文标题:redis实现分布式锁

      本文链接:https://www.haomeiwen.com/subject/tloutrtx.html