redis分布式锁

学习目的:

  1. 在商品抢购的时候怎么解决线程安全问题?
  2. 分布式锁的基本原理是什么?
  3. 怎么杜绝redis使用时死锁问题?
  4. 如何解决redis分布式锁中的锁失效问题?
  5. redis分布式锁中,刚创建一个key,还没来得及主从复制,redis宕机了,怎么解决数据不一致问题?
  6. 如何将redis效率成倍提升?

用例代码:

//spring内部提供的 模板类 
@Autowired
    private StringRedisTemplate stringRedisTemplate;


    @RequetMapping("/reduceProduce")
    public String reduceProduce() throws Exception {

        // 从redis中获取库存数量 相当于 命令: jedis. get("produceNum");
        int produceNum = Integer.parselnt(stringRedisTemplate.opsForValue().get("produceNum"));

        if (produceNum > 0) {
            int remainProduceNum = produceNum - 1;
            //  库存数量减一  相当于操作 :  jedis. set (key, value)
            stringRedisTemplate.opsForValue().set("produceNum", remainProduceNum + "");
            System.out.println("扣减成功,剩余剩余:" + remainProduceNum + "");
        } else {
            System.out.println("扣减失败,库存不足");
        }


        return "end";
    }
在商品抢购的时候怎么解决线程安全问题?

让我们想一想上面用例代码存在什么问题?

是的。存在线程安全问题,当多个用户进行访问的时候,或造成重复扣除库存的问题

怎么解决呢?让我们看一下接下来的代码用例
 @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @RequetMapping("/reduceProduce")
    public String reduceProduce() throws Exception {


        synchronized (this) {
            // 从redis中获取库存数量 相当于 命令: jedis. get("produceNum");
            int produceNum = Integer.parselnt(stringRedisTemplate.opsForValue().get("produceNum"));

            if (produceNum > 0) {
                int remainProduceNum = produceNum - 1;
                //  库存数量减一  相当于操作 :  jedis. set (key, value)
                stringRedisTemplate.opsForValue().set("produceNum", remainProduceNum + "");
                System.out.println("扣减成功,剩余剩余:" + remainProduceNum + "");
            } else {
                System.out.println("扣减失败,库存不足");
            }
        }

        return "end";
    }

于是我们想到了使用synchronized关键字,给其进行加锁。

但是上面代码只适合在单体架构中使用。当你是分布式架构和微服务架构的时候,要怎么解决呢。每一个项目运行时是作用于自己独立的jvm中,每个项目中的synchronized只作用于自己的jvm中(又回到了上述的线程安全问题了)。所以如果你是单体架构放心使用进行,如果你是分布式架构或者微服务架构,就要引入接下来的redis分布式架构了。 接下来让我们了解一下redis分布式锁的运行原理

分布式锁的基本原理

正常redis的时候    set key value
    
redis锁使用       setnx key  value
    
区别:set key value。当你第二次进行set相同key会将值进行覆盖,无论如何都会进行减库存操作。 
    setnx  key value。当你第二次进行set相同key会判断该key是否存在,如果存在就不进行减库存操作,如果不存在就进行减库存操作。
运用分布式锁修改后的代码
 @Autowired
    private StringRedisTemplate stringRedisTemplate;


    @RequetMapping("/reduceProduce")
    public String reduceProduce() throws Exception {

       try{
        
        //相当于执行了    jedis. setnx 的指令  执行成功返回true,执行失败返回false
        Boolean result = stringRedisTemplate.opsForValue().setIfAbsent("produceNum","库存数量");

        if (!result){
            return "活动太火爆,请稍后重试";
        }

            // 从redis中获取库存数量 相当于 命令: jedis. get("produceNum");
            int produceNum = Integer.parselnt(stringRedisTemplate.opsForValue().get("produceNum"));

            if (produceNum > 0) {
                int remainProduceNum = produceNum - 1;
                //  库存数量减一  相当于操作 :  jedis. set (key, value)
                stringRedisTemplate.opsForValue().set("produceNum", remainProduceNum + "");
                System.out.println("扣减成功,剩余剩余:" + remainProduceNum + "");
            } else {
                System.out.println("扣减失败,库存不足");
            }
       }finally{
        //释放锁
        stringRedisTemplate.delete("produceNum");
       }
        return "end";
    }

怎么杜绝redis使用时死锁问题

上面代码如果在执行 if (produceNum > 0) {} 这一行代码的时候,服务突然挂了,finally中释放锁的操作还未进行,当你再次进行访问的时候,该key的商品还是存在的,无论如何都是访问不到的,这个时候就会造成死锁问题。如果解决呢?让我们看看接下来的代码。

@Autowired
    private StringRedisTemplate stringRedisTemplate;


    @RequetMapping("/reduceProduce")
    public String reduceProduce() throws Exception {

        try {
            //相当于执行了    jedis. setnx 的指令  执行成功返回true,执行失败返回false   设置过期时间
            Boolean result = stringRedisTemplate.opsForValue().setIfAbsent ("produceNum", "库存数量",  10, TimeUnit. SECONDS);
            if (!result){
                return "活动太火爆,请稍后重试";
            }

            // 从redis中获取库存数量 相当于 命令: jedis. get("produceNum");
            int produceNum = Integer.parselnt(stringRedisTemplate.opsForValue().get("produceNum"));

            if (produceNum > 0) {
                int remainProduceNum = produceNum - 1;
                //  库存数量减一  相当于操作 :  jedis. set (key, value)
                stringRedisTemplate.opsForValue().set("produceNum", remainProduceNum + "");
                System.out.println("扣减成功,剩余剩余:" + remainProduceNum + "");
            } else {
                System.out.println("扣减失败,库存不足");
            }
        }finally {
            //释放锁操作
            stringRedisTemplate.delete("produceNum");
        }


        return "end";
    }

是的,给其添加一个过期时间即可。所以在使用redis的时候,一定要注意给其添加一个过期时间和进行释放锁的操作。

如何解决redis分布式锁中的锁失效问题?

可能造成锁失效的情况
  • 在事物内务使用锁,锁在事物提交前释放

在这里插入图片描述

解决方案:在上锁之前,在事物的外层上锁。(新建一个方法,调用事物方法)

  • 业务未执行完,锁提前释放

例如:上锁10秒钟,业务流程执行15秒,就可能造成锁失效的问题

解决方案:1.将锁的过期时间设置的长一点 2.使用redisson的watchdog机制给锁续命,给锁一个uuId的标识,没等过期时间进行三分之一进行.通过uuid进行判断是否是自己的锁进行续命。

redis分布式锁如何保证数据的一致性

redis分布式锁会造成数据缺失,但是效率很高。

所以 如果能允许少量数据的缺失保证高的效率使用redis分布式锁。如果不允许少量数据的缺失,可以使用zookeeper分布式锁。

如何将redis效率成倍提升?

这里可以使用分段锁提升redis的效率。

锁续命,给锁一个uuId的标识,没等过期时间进行三分之一进行.通过uuid进行判断是否是自己的锁进行续命。

redis分布式锁如何保证数据的一致性

redis分布式锁会造成数据缺失,但是效率很高。

所以 如果能允许少量数据的缺失保证高的效率使用redis分布式锁。如果不允许少量数据的缺失,可以使用zookeeper分布式锁。

如何将redis效率成倍提升?

这里可以使用分段锁提升redis的效率。

分段锁:之前存商品 100个 ,采用分段锁后是 key1 10; key2 10; key3 10;… key10 10;配合lua脚本使用。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值