分布式锁(基于Redis实现)

操作小记:进入github的redisson后,选择如下图,可以进入Redisson的项目介绍(中文文档)在这里插入图片描述

一、分布式锁的概念

分布式锁的目的是:在分布式环境下,为了某一操作(从数据库中,插入一条数据)只执行一次,避免数据操作的重复导致的一系列问题,使用了Redis中的setnx命令和lua脚本,来实现分布式锁,解决以上问题。

二、使用setnx命令的几种情况

2.1、如果业务代码出现异常,执行释放锁(删除锁)的命令不能执行,导致死锁(没有释放锁),该怎么办?

解决办法是:可以用try{}finally{}来解决,但是如果执行到业务代码,机器突然断电了,该怎么办?
最终解决办法是:设置锁的过期时间,即使没有删除锁,到期锁自动就会删除
在这里插入图片描述

2.2、设置锁和设置过期时间之间进行闪断(宕机),也就依然成为了死锁,该怎么解决?

解决办法是:用Redis的setnx ex命令,该命令是设置锁+设置过期时间的合并,也是一个原子操作。

在这里插入图片描述

2.3、删除锁的时候,由于业务代码超时,导致锁提早过期,删除了别人的锁,该怎么办?
2.4、如果业务超时的话,也会导致其他线程拿到该锁,进入该业务操作,导致业务重复操作

解决以上问题:占锁的时候,将值设为uuid,删除的时候,进行比对

在这里插入图片描述

2.5、由于比对uuid的值(要从Redis中获取值),并且删除该值,不是原子操作,在获取uuid值时,可能费时,导致最后删除的别的线程的锁,该怎么办?

解决办法:比对uuid值+比对成功后删除值=原子操作,可以通过Redis的官方文档知道,可以使用lua脚本

在这里插入图片描述
在这里插入图片描述

2.6、最后形态如下:加锁和解锁的时候,保证原子性

在这里插入图片描述
最终代码:

 @Test
    public void testRedisHadoop(){
    
    
        String uuid = UUID.randomUUID().toString();
//        占锁(设置值),并且设置过期时间。这是一个Redis的原子命令
//        NX和EX
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 300, TimeUnit.SECONDS);

        if(lock){
    
    
//            加锁成功,执行业务
            try{
    
    
                System.out.println("业务执行。。。。");
            }finally {
    
    
                //            获取值对比+对比成功处理=原子操作------lua脚本
//            通过uuid来删除锁,以防删除别人的锁或者
//            String lockValue = (String) redisTemplate.opsForValue().get("lock");
//            if(uuid.equals(lockValue)){
    
    
//                redisTemplate.delete("lock");
//            }

                //lua脚本
                String script = "if redis.call(\"get\",KEYS[1]) == ARGV[1]\n" +
                        "then\n" +
                        "    return redis.call(\"del\",KEYS[1])\n" +
                        "else\n" +
                        "    return 0\n" +
                        "end";

                //删除锁
                Object lock1 = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);

            }




        }else {
    
    
            System.out.println("获取锁失败,继续尝试");
            testRedisHadoop();//自旋锁
        }


    }

三、Redisson的分布式锁--------可重入锁

如果a方法内调用了b方法,a加了一把标志为1的锁,b也加了一把标志为1的锁。
如果是**可重入锁,那么执行a方法时,里面的b方法不用等a释放锁,直接拿来a方法的锁使用。
如果是
不可重入锁,**那么执行a方法时,里面的b方法需要等a释放1锁后,才去拿1锁,然后才执行。

3.1、可重入锁的两个加锁重载方法(具体如下图)

在这里插入图片描述

四、redis的读写锁

作用是:获得最新的数据

写锁是一个排它锁(互斥锁,独享锁),读锁是一个共享锁。

写锁在操作时,读锁只能等待。如果没有写锁在操作,读锁就是共享的,和没加锁是一样的。

在这里插入图片描述

4.2、读写锁补充说明

读+写:有读锁,写需要等待。=======当读锁正在进行时,写锁需要等待读锁释放(执行完),才能执行。
读+读:相当于无锁状态。所有的当前的读锁,他们都会同时加锁成功。
写+读:读锁等待写锁释放
写+写:阻塞方式(必须拿到为止)
总结:只要有写锁,都必须等待。

五、redis的闭锁

在这里插入图片描述

六、redis的信号量(Semaphore)

6.1、基本使用原理:

在这里插入图片描述

6.2、可以用作限流,具体原理如下:

在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/weixin_43983411/article/details/110679363