Redis分布式锁完整使用

简单了解什么是分布式锁

比如:
白话文就是、如果使用本地锁(Synchronized)的话、在微服务中、单个服务是可以锁的、假如开多个实例、通过网关进行访问、多个实例A、B、C、D 服务都会从新查询、锁不住多个实例、
分布式锁就是解决这一问题、多个实例共享同一个中间件里面的值(redis)

在这里插入图片描述

在这里插入图片描述

分布式锁会出现集中问题、带着问题码着代码肯定是思路清晰到你懂的

分布式锁产生的(5中)问题

1)、阶段一
setnx占好了位、业务代码异常或者程序在页面过程中宕机、没有执行删除逻辑、这就造成了死锁
解决:
设置锁的自动过期、即使没有删除、也会自动过期

2)、进阶到阶段二
获取到锁之后、就设置过期时间
1、sentnx设置好、正要去设置过期时间、宕机又死锁了
解决:
设置过期时间和占位必须是原子的、redis支持使用sentnx ex命令

Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent(“lock”, “111”, 300, TimeUnit.SECONDS);

3)、进阶到阶段三
1、删除锁直接删除??
如果由于业务时间很长、锁自己过期了,我们直接删除、有可能把别人正在持有的锁删除了
解决:
站锁的时候、值指定为uuid,每个人匹配自己的锁才删除(保证删锁的时候不是别人的锁)

  String uuid = UUID.randomUUID().toString();
    //设置过期时间、必须和加索是同步的、原子的、要不全成功、要不全失败
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
    String lockValue = stringRedisTemplate.opsForValue().get("lock");
            if(uuid.equals(lockValue)){
                stringRedisTemplate.delete("lock");
            }

4)、进阶到阶段四
如果正好判断是当前值、正要删除锁的时候,锁已经过期了,别人已经设置到了新值、那吗我们删除的是别人的锁
解决:
删除锁必须保证原子性。使用redis+Lua脚本完成、带删除线 ~~ 就是分布式锁

 public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() {

        /**
         * 1、将数据库的多次查询变为一次
         * 改造业务
         *
         * 占分布式锁、去redis占坑
         */
        ~~String uuid = UUID.randomUUID().toString();
        //设置过期时间、必须和加索是同步的、原子的、要不全成功、要不全失败
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);~~ 
        if (lock) {
            System.out.println("获取分布式锁成功");
            Map<String, List<Catelog2Vo>> dataFromDb;
            try {
                //加锁成功 ---执行业务 ----也得先看缓存中有没有
                dataFromDb = getDataFromDb();
            } finally {
                /**
                 * redis 如果获取 指定的lock(KEYS[1]) key == 指定的 uuid (ARGV[1]) 值
                 * 然后就调用 redis (then return redis.call('del',KEYS[1])) 删除方法 把这个key(KEYS[1])) 删除
                 * 否则就返回0 (else return 0 end")
                 * 而且删除成功返回的是 1
                 */
                ~~String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";~~ 
                //原子删锁
                ~~Long lock1 = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);~~ 

            }
            //设置过期时间、必须和加索是同步的、原子的、要不全成功、要不全失败
            //stringRedisTemplate.expire("lock",30,TimeUnit.SECONDS); //设置过期时间

            //执行删除redis 问题:不是原子性----使用Lua脚本
//            String lockValue = stringRedisTemplate.opsForValue().get("lock");
//            if(uuid.equals(lockValue)){
//                stringRedisTemplate.delete("lock");
//            }

            return dataFromDb;
        } else {
            System.out.println("获取分布式锁不成功");
            //加锁失败。。。重试--还是的重新站锁-直接重新在调用一下自己的方法
            return getCatalogJsonFromDbWithRedisLock();//自旋的方式、相当于synchrozied他会监听到你释放锁之后立马在占锁
        }

    }

5)、阶段五-最终形态
保证加锁 【占位+过期时间】和删除锁 【判断+删除】 的原子性。更难的事情,锁的自动续期
redis分布式锁两大核心
①、加锁保证原子性
②、解锁保证原子性
这样才能锁住所有的服务

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

redis的分布式锁解决文档描述

其实redis布式锁就这两步、可以根据自己的业务进行改造
主要方面:
1、原子性加锁
2、原子性解锁

    解释下面的原子性解锁的脚本
    redis 如果获取 指定的lock(KEYS[1]) key == 指定的 uuid (ARGV[1]) 值
    然后就调用 redis (then return redis.call('del',KEYS[1])) 删除方法 把这个key(KEYS[1])) 删除
    否则就返回0else return 0 end")
    而且删除成功返回的是 1
   String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
public Map<String, List<Catelog2Vo>> getCatalogJsonFromDbWithRedisLock() {

        /**
         * 占分布式锁、去redis占坑
         */
        String uuid = UUID.randomUUID().toString();
        //设置过期时间、必须和加索是同步的、原子的、要不全成功、要不全失败
        Boolean lock = stringRedisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);
        if (lock) {
            System.out.println("获取分布式锁成功");
            Map<String, List<Catelog2Vo>> dataFromDb;
            try {
                //加锁成功 ---执行业务 ----也得先看缓存中有没有
                dataFromDb = getDataFromDb();
            } finally {
                String script = "if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";
                //原子删锁
                Long lock1 = stringRedisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);
            }
            return dataFromDb;
        } else {
            System.out.println("获取分布式锁不成功");
            //加锁失败。。。重试--还是的重新站锁-直接重新在调用一下自己的方法
            return getCatalogJsonFromDbWithRedisLock();//自旋的方式、相当于synchrozied他会监听到你释放锁之后立马在占锁
        }
    }

展示效果图
在这里插入图片描述

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值