redis深入浅出分布式锁实现下篇

 更新时间:2022年08月30日 17:10:40   作者:一个风轻云淡  
在单体应用中,如果我们对共享数据不进行加锁操作,会出现数据一致性问题,我们的解决办法通常是加锁。下面我们一起聊聊使用redis来实现分布式锁

优化之UUID防误删

问题:删除操作缺乏原子性。

场景:

index1执行删除时,查询到的lock值确实和uuid相等

uuid=v1

set(lock,uuid);

index1执行删除前,lock刚好过期时间已到,被redis自动释放,在redis中没有了lock,没有了锁。

index2获取了lock

index2线程获取到了cpu的资源,开始执行方法

uuid=v2

set(lock,uuid);

index1执行删除,此时会把index2的lock删除

index1 因为已经在方法中了,所以不需要重新上锁。index1有执行的权限。index1已经比较完成了,这个时候,开始执行

删除的index2的锁!

优化之LUA脚本保证删除的原子性

@GetMapping("testLockLua")
public void testLockLua() {
    //1 声明一个uuid ,将做为一个value 放入我们的key所对应的值中
    String uuid = UUID.randomUUID().toString();
    //2 定义一个锁:lua 脚本可以使用同一把锁,来实现删除!
    String skuId = "25"; // 访问skuId 为25号的商品 100008348542
    String locKey = "lock:" + skuId; // 锁住的是每个商品的数据
   // 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 类型,
        // 那么返回字符串与0 会有发生错误。
        redisScript.setResultType(Long.class);
        // 第一个要是script 脚本 ,第二个需要判断的key,第三个就是key所对应的值。
        redisTemplate.execute(redisScript, Arrays.asList(locKey), uuid);
    } else {
        // 其他线程等待
        try {
            // 睡眠
            Thread.sleep(1000);
            // 睡醒了之后,调用方法。
            testLockLua();
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

Lua 脚本详解:

项目中正确使用

定义key,key应该是为每个sku定义的,也就是每个sku有一把锁。

String locKey ="lock:"+skuId; // 锁住的是每个商品的数据
Boolean lock = redisTemplate.opsForValue().setIfAbsent(locKey, uuid,3,TimeUnit.SECONDS);

总结

加锁

使用lua释放锁

重试

为了确保分布式锁可用,我们至少要确保锁的实现同时满足以下四个条件:

- 互斥性。在任意时刻,只有一个客户端能持有锁。

- 不会发生死锁。即使有一个客户端在持有锁的期间崩溃而没有主动解锁,也能保证后续其他客户端能加锁。

- 解铃还须系铃人。加锁和解锁必须是同一个客户端,客户端自己不能把别人加的锁给解了。

- 加锁和解锁必须具有原子性

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

相关文章

  • 浅谈java String不可变的好处

    浅谈java String不可变的好处

    这篇文章主要介绍了java String不可变的好处,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-03-03
  • Spring Boot Actuator未授权访问漏洞的问题解决

    Spring Boot Actuator未授权访问漏洞的问题解决

    Spring Boot Actuator 端点的未授权访问漏洞是一个安全性问题,可能会导致未经授权的用户访问敏感的应用程序信息,本文就来介绍一下解决方法,感兴趣的可以了解一下
    2023-09-09
  • java webservice超时时间设置方法代码

    java webservice超时时间设置方法代码

    当我们使用WebService进行调用时,有时会出现超时的情况,下面这篇文章主要给大家介绍了关于java webservice超时时间设置方法的相关资料,文中通过代码介绍的非常详细,需要的朋友可以参考下
    2024-01-01
  • Java对List进行排序的方法总结

    Java对List进行排序的方法总结

    在Java中,对List进行排序是一项常见的任务,Java提供了多种方法来对List中的元素进行排序,本文将详细介绍如何使用Java来实现List的排序操作,涵盖了常用的排序方法和技巧,需要的朋友可以参考下
    2024-07-07
  • Spring MVC学习教程之RequestMappingHandlerAdapter详解

    Spring MVC学习教程之RequestMappingHandlerAdapter详解

    这篇文章主要给大家介绍了关于Spring MVC学习教程之RequestMappingHandlerAdapter的相关资料,文中通过示例代码介绍的非常详细,需要的朋友可以参考借鉴,下面随着小编来一起学习学习吧
    2018-11-11
  • java对象拷贝详解及实例

    java对象拷贝详解及实例

    这篇文章主要介绍了java对象拷贝详解及实例的相关资料,需要的朋友可以参考下
    2017-04-04
  • 如何使用Spring integration在Springboot中集成Mqtt详解

    如何使用Spring integration在Springboot中集成Mqtt详解

    MQTT是多个客户端通过一个中央服务器传递信息的多对多协议,能高效地将信息分发给一个或多个订阅者,下面这篇文章主要给大家介绍了关于如何使用Spring integration在Springboot中集成Mqtt的相关资料,需要的朋友可以参考下
    2023-02-02
  • 【Redis缓存机制】详解Java连接Redis_Jedis_事务

    【Redis缓存机制】详解Java连接Redis_Jedis_事务

    这篇文章主要介绍了【Redis缓存机制】详解Java连接Redis_Jedis_事务,详细的介绍了Jedis事务和实例,有兴趣的可以了解一下。
    2016-12-12
  • Spring声明式事务和@Aspect的拦截顺序问题的解决

    Spring声明式事务和@Aspect的拦截顺序问题的解决

    本篇文章主要介绍了Spring声明式事务和@Aspect的拦截顺序问题的解决,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2018-05-05
  • Java基础之文件概述

    Java基础之文件概述

    这篇文章主要介绍了Java基础之文件概述,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有一定的帮助,需要的朋友可以参考下
    2021-05-05

最新评论