Redis分布式锁的简单实现_使用setnx和del命令实现redis分布式锁(1)

img
img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上大数据知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、讲解视频,并且后续会持续更新

需要这份系统化资料的朋友,可以戳这里获取

那么加锁之后我们如何释放锁呢,此时就需要调用del命令删除key,以此来释放锁。

image-20210503212618173

也就是将对应的key删除掉就释放了相应的锁了。

所以,redis实现分布式锁需要setnx和del这两个指令来完成加锁和解锁。

相关问题

1、当我们加锁以后忘记释放锁,那么其他应用将一直无法获取到锁,这是就产生了问题。那么这个问题怎么解决呢?

我们可以在加锁之后设置锁的存活时间,存活时间到达之后,锁会自动失效,我们无需手动删除key。

例如:

image-20210503212953516

此时我们就可以放心使用分布式锁了。

2、上面虽然实现了加锁以及设置过期时间,但是如果我们加锁以后,设置过期时间之前,redis突然宕机了,没有执行expire指令,当redis重启以后,该锁还是会一直存在,所以我们需要使用原子操作来加锁并且设置过期时间,那么可以通过一条指令搞定:

set lock yes nx ex 20

image-20210503213206888

这样我们就可以放心使用redis分布式锁啦!

redis分布式锁优化-UUID防误删

首先我们来看看redis分布式锁的流程:

  1. 加锁并设置过期时间
  2. 进行业务处理
  3. del操作释放锁

假如此时我们的A服务在第2步由于机器的原因卡住了,并且卡顿的时间超过了我们设置的过期时间。此时,B服务获得了相同的分布式锁。

此时A服务从卡顿状态恢复,并且处理完业务,继续执行了del操作,那么此时del操作删除的是B服务的分布式锁,这会导致一系列的问题。

这种问题如何解决呢,其实解决的方式很简单:

  1. 加锁并设置过期时间,加锁时需要设置锁的值为UUID(其实不是UUID也可以,只要不同的服务产生的值不同即可,主要作用就是标识不同的加锁对象)
  2. 进行业务处理
  3. 首先获取锁的值,判断是否与自身的UUID值相等,如果相等则证明这是我们加的分布式锁,可以进行删除,否则就不能删除,因为这是其他服务加的锁。

具体代码很简单,这里不再展示啦!

redis分布式锁优化-原子性操作实现锁的删除操作

其实我们使用UUID删除分布式锁的话还是存在一定的问题:

当A服务判断了分布式锁的UUID与自身的UUID值相等,那就准备进行删除操作,但是此时,分布式锁正好过期,B服务获得了这个分布式锁,A服务还是会进行del操作删除分布式锁,那么又成了删除B服务的分布式锁。这里又产生了问题,那么我们如何解决呢?

我们可以通过原子性操作来解决这个问题。

通过如下代码可以使用Lua脚本实现原子操作:

@GetMapping("testLockLua")
public void testLockLua() {
    //1 声明一个uuid ,将做为一个value 放入我们的key所对应的值中
    String uuid = UUID.randomUUID().toString();
    //2 定义一个锁:lua 脚本可以使用同一把锁,来实现删除!
    String locKey = "lock"; // 锁住的是每个商品的数据

    // 3 获取锁
    Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid, 3, TimeUnit.SECONDS);

    // 第一种: lock 与过期时间中间不写任何的代码。
    // redisTemplate.expire("lock",10, TimeUnit.SECONDS);//设置过期时间
    // 如果true
    if (lock) {
        // 执行的业务逻辑开始
        // 获取缓存中的num 数据
        Object value = redisTemplate.opsForValue().get("num");
        // 如果是空直接返回
        if (StringUtils.isEmpty(value)) {
            return;
        }
        // 不是空 如果说在这出现了异常! 那么delete 就删除失败! 也就是说锁永远存在!
        int num = Integer.parseInt(value + "");
        // 使num 每次+1 放入缓存
        redisTemplate.opsForValue().set("num", String.valueOf(++num));
        /\*使用lua脚本来锁\*/
        // 定义lua 脚本
        String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
        // 使用redis执行lua执行
        DefaultRedisScript<Long> redisScript = new DefaultRedisScript<>();
        redisScript.setScriptText(script);
        // 设置一下返回值类型 为Long
        // 因为删除判断的时候,返回的0,给其封装为数据类型。如果不封装那么默认返回String 类型,


![img](https://img-blog.csdnimg.cn/img_convert/b4f6cb48f3012a570d26055d2cfbbc77.png)
![img](https://img-blog.csdnimg.cn/img_convert/5ff32d0f3c6caa6e892479bc0525b097.png)

**网上学习资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。**

**[需要这份系统化资料的朋友,可以戳这里获取](https://bbs.csdn.net/topics/618545628)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

tps://bbs.csdn.net/topics/618545628)**


**一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人,都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!**

  • 4
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值