redis和redisson实现分布式锁的操作方法

 更新时间:2024年03月27日 10:48:51   作者:开心就好啦啦啦  
使用 Redis 实现分布式锁,最直接的想法是利用 setnx 和 expire 命令实现加锁,这篇文章主要介绍了redis和redisson实现分布式锁的操作方法,需要的朋友可以参考下
(福利推荐:【腾讯云】服务器最新限时优惠活动,云服务器1核2G仅99元/年、2核4G仅768元/3年,立即抢购>>>:9i0i.cn/qcloud

(福利推荐:你还在原价购买阿里云服务器?现在阿里云0.8折限时抢购活动来啦!4核8G企业云服务器仅2998元/3年,立即抢购>>>:9i0i.cn/aliyun

基于setnx命令的分布式锁

1. 加锁

使用 Redis 实现分布式锁,最直接的想法是利用 setnx 和 expire 命令实现加锁。

在 Redis 中,setnx 是「set if not exists」如果不存在,则 setnx 的意思,当一个线程执行 setnx 返回 1,说明 key 不存在,该线程获得锁;当一个线程执行 setnx 返回 0,说明 key 已经存在,那么获取锁失败。

SETNX lockKey uniqueValue
(integer) 1
SETNX lockKey uniqueValue
(integer) 0

2. 释放锁

释放锁的话,直接通过 DEL 命令删除对应的 key 即可。

DEL lockKey
(integer) 1

为了防止误删到其他的锁,这里我们建议使用 Lua 脚本通过 key 对应的 value(唯一值)来判断。选用 Lua 脚本是为了保证解锁操作的原子性。因为 Redis 在执行 Lua 脚本时,可以以原子性的方式执行,从而保证了锁释放操作的原子性。

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

释放锁时,先比较锁对应的 value 值是否相等,value值可以在加锁的时候把当前的线程 ID 当做value,并在删除之前验证 key 对应的 value 是不是自己线程的 ID,避免锁的误释放

3. setnx缺点

setnx 的 key 必须设置一个超时时间,以保证即使没有被显式释放,这把锁也要在一定时间后自动释放。可以使用expire命令设置锁超时时间

4. 存在问题:

setnx 和 expire 不是原子性的操作,假设某个线程执行setnx 命令,成功获得了锁,但是还没来得及执行expire 命令,服务器就挂掉了,这样一来,这把锁就没有设置过期时间了,变成了死锁,别的线程再也没有办

法获得锁了。
解决方案:redis的set命令支持在获取锁的同时设置key的过期时间

基于set命令的分布式锁

Redis 从 2.6.12 起,SET 涵盖了 SETEX 的功能,并且 SET 本身已经包含了设置过期时间的功能

使用set命令加锁并设置锁过期时间:

127.0.0.1:6379> SET lockKey uniqueValue EX 3 NX
OK

lockKey:加锁的锁名;
uniqueValue:能够唯一标示锁的随机字符串;
NX:只有当 lockKey 对应的 key 值不存在的时候才能 SET 成功;
EX:过期时间设置(秒为单位)EX 3 标示这个锁有一个 3 秒的自动过期时间。与 EX 对应的是 PX(毫秒为单位),这两个都是过期时间设置。
一定要保证设置指定 key 的值和过期时间是一个原子操作!!! 不然的话,依然可能会出现锁无法被释放的问题。

不过,这种解决办法同样存在漏洞:如果操作共享资源的时间大于过期时间,就会出现锁提前过期的问题,进而导致分布式锁直接失效。如果锁的超时时间设置过长,又会影响到性能。

为了解决这个问题,我们可以让获得锁的线程开启一个守护线程,用来给快要过期的锁“续期”。在JAVA的Redisson包中有一个”看门狗”机制,已经帮我们实现了这个功能。

redission看门狗分布式锁

Redisson 中的分布式锁自带自动续期机制,使用起来非常简单,原理也比较简单,其提供了一个专门用来监控和续期锁的 Watch Dog( 看门狗),如果操作共享资源的线程还未执行完成的话,Watch Dog 会不断地延长锁的过期时间,进而保证锁不会因为超时而被释放。

1.加锁机制

  • 线程去获取锁,获取成功:执行lua脚本,保存数据到redis数据库。
  • 此时另外一个线程去获取锁,可以一直通过while循环尝试获取锁(锁重试)
    如果在获取锁的最大等待时间内加锁成功,执行lua脚本,保存数据到redis数据库。如果失败,则返回加锁失败。

2.watch dog自动延期机制:

Redisson在获取锁之后,会维护一个看门狗线程在每一个锁设置的过期时间的1/3处,如果线程还没执行完任务,则不断延长锁的有效期。刚开始锁的过期时间默认是30秒,可以通过 lockWactchdogTimeout 参数来改变。

每过 10 秒,看门狗就会执行续期操作,将锁的超时时间重置为 30 秒。看门狗续期前也会先判断是否需要执行续期操作,需要才会执行续期,否则取消续期操作。

看门狗启动后,对整体性能也会有一定影响,默认情况下看门狗线程是不启动的。如果使用redisson进行加锁的同时设置了锁的过期时间,也会导致看门狗机制失效。

加锁的时间默认是30秒,如果加锁的业务没有执行完,那么每隔 30 ÷ 3 = 10秒,就会进行一次续期,把锁重置成30秒,保证解锁前锁不会自动失效。

3.redisson分布式锁的关键点:

  • 对key不设置过期时间,由Redisson在加锁成功后给维护一个watchdog看门狗,watchdog负责定时监听并处理,在锁没有被释放且快要过期的时候自动对锁进行续期,保证解锁前锁不会自动失效
  • 通过Lua脚本实现了加锁和解锁的原子操作,底层是使用setnx和lua脚本
  • 通过记录获取锁的客户端id,每次加锁时判断是否是当前客户端已经获得锁,实现了可重入锁。

Redisson 的分布式可重入锁 RLock 为例来说明如何使用 Redisson 实现分布式锁:

public String testLock() throws InterruptedException {
    RLock lock = redissonClient.getLock("it_lock");
    //尝试获取锁,tryLock参数分别是:获取锁的最大等待时间(期间重试) ,锁自动释放时间,时间单位
    //lock.tryLock(10, 30,TimeUnit.SECONDS); //设置锁释放时间 不会续期操作
    boolean isLock = lock.tryLock(10, TimeUnit.SECONDS); //没有设置锁释放时间 守护线程会自动续期
    //是否成功
    if(isLock){
        try {
            //业务逻辑
        }finally {
            lock.unlock();
        }
    }
    return "finish";
}

指定锁超时时间,不会使用自动续期机制

lock.tryLock(10, 30,TimeUnit.SECONDS); //设置锁释放时间 不会续期操作

只有未指定锁超时时间,才会使用到 Watch Dog 自动续期机制。

   // 手动给锁设置过期时间,不具备 Watch Dog 自动续期机制
   lock.tryLock(10, TimeUnit.SECONDS);

如果使用 Redis 来实现分布式锁的话,还是比较推荐直接基于 Redisson 来做的

到此这篇关于redis和redisson实现分布式锁的文章就介绍到这了,更多相关redis和redisson实现分布式锁内容请搜索程序员之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持程序员之家!

相关文章

  • Redis缓存空间优化实践详解

    Redis缓存空间优化实践详解

    缓存Redis,是我们最常用的服务,其适用场景广泛,被大量应用到各业务场景中。也正因如此,缓存成为了重要的硬件成本来源,我们有必要从空间上做一些优化,降低成本的同时也会提高性能,本文通过代码示例介绍了redis如何优化缓存空间,需要的朋友可以参考一下
    2023-04-04
  • 基于Redis6.2.6版本部署Redis?Cluster集群的问题

    基于Redis6.2.6版本部署Redis?Cluster集群的问题

    这篇文章主要介绍了基于Redis6.2.6版本部署Redis?Cluster集群,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2022-04-04
  • redis加锁的三种方式小结

    redis加锁的三种方式小结

    本文主要介绍了redis加锁的三种方式小结,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • Redis分布式锁的实现方式

    Redis分布式锁的实现方式

    本文主要介绍了Redis分布式锁的实现方式,分布式锁是?满足分布式系统或集群模式下多进程可见并且互斥的锁。感兴趣的同学可以参考阅读
    2023-04-04
  • 基于Redis验证码发送及校验方案实现

    基于Redis验证码发送及校验方案实现

    本文主要介绍了基于Redis验证码发送及校验方案实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2023-01-01
  • redis中Could not get a resource from the pool异常及解决方案

    redis中Could not get a resource from

    这篇文章主要介绍了redis中Could not get a resource from the pool异常及解决方案,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2022-12-12
  • Redis基本数据类型Set常用操作命令

    Redis基本数据类型Set常用操作命令

    这篇文章主要为大家介绍了Redis基本数据类型Set常用操作命令,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-05-05
  • Redis内存碎片原理深入分析

    Redis内存碎片原理深入分析

    这篇文章主要为大家介绍了Redis内存碎片原理深入分析,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2023-02-02
  • linux 常见的标识与Redis数据库详解

    linux 常见的标识与Redis数据库详解

    这篇文章主要介绍了linux 常见的标识与Redis数据库,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2020-10-10
  • Redis 密码设置和查看密码的方法

    Redis 密码设置和查看密码的方法

    这篇文章主要介绍了Redis 密码设置和查看密码的方法,非常不错,具有参考借鉴价值,需要的朋友可以参考下
    2018-04-04

最新评论

?


http://www.vxiaotou.com