Redis的分布式锁实现原理

分布式锁实现原理:

​ 通常在单体架构的时候,解决多线程问题,可以直接使用synchronized,或者lock锁,只要锁保证时同一把锁,此时就能够解决代码的线程问题。

 Lock lock=new ReentrantLock();

    @Autowired
    private RedisTemplate redisTemplate;

    @Override
    public void lockTest() {
     

        lock.lock();
        String num = (String) redisTemplate.opsForValue().get("num");

        Integer intNum=Integer.parseInt(num);
        intNum=intNum+1;
        redisTemplate.opsForValue().set("num",intNum.toString());
        lock.unlock();
    }

​ 而在分布式架构中,继续使用synchronized和lock锁,就不能保证锁对象还是同一个对象,此时就不能保证操作的多线程正常执行。有两种方式实现分布式锁:1,zookeeper实现分布式锁;2,redis中的setnx来实现分布式锁。

    @Autowired
    private RedisTemplate<String, String> redisTemplate;
    @Override
    public void redisIncr() {
     


        // 并没有准备服务续约的操作  暂时为了解决问题,先将锁的存在时间尽量设置畅一点300s 但是:服务预约把这个时间设置的长,其实不太好
                                              //  自旋的时间其实不好控制
                                   
        String randomId = UUID.randomUUID().toString();
        //被封装了,返回一个boolean,正常的setNX返回0和1,抢到返回0
        Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", randomId, 300, TimeUnit.SECONDS);
        // uuid  -> 之前保存的value 是没有意义的,就可能发生,锁的误删
        if (lock) {
     
            String num = redisTemplate.opsForValue().get("num");
            if (num != null) {
     
                Integer intNum = Integer.parseInt(num);
                intNum = intNum + 1;
                redisTemplate.opsForValue().set("num", intNum.toString());
                String lockRedis = redisTemplate.opsForValue().get("lock");
                
                String luaString = "if redis.call('get',KEYS[1]) == ARGV[1] then  return redis.call('del',KEYS[1])  else  return 0 end";
                redisTemplate.execute(new DefaultRedisScript<Long>(luaString, Long.class), Arrays.asList("lock"),lockRedis);
                //判断一下:当前要删这把锁是不是自己的锁->采用此种方法比较锁对象:当线程1设置过期时间为10s时,当我们在9.9秒的时候从Redis取出数据,此时数据还是当前线程1的值,但是数据传输需要时间,假设1s,那么锁删除的时间就变成了在10.9s的时候才删除锁对象;但是可能在10s之后,其他线程2就已经从新抢到了锁,执行了代码,此时Redis中的lock数据,就已经变为了线程2的值,但是由于在10.9s时,线程1会执行删除Lock,导致线程2可能才刚那到锁,就被删除了,从而引发数据异常.  所以此时我们可以采用lua脚本来对比锁对象,保证锁一一对应
           /*     if(randomId.equals(lockRedis)){
                    redisTemplate.delete("lock");
                }*/
            }
        } else {
     
            try {
     
                Thread.sleep(100);//不设置自旋时间
                redisIncr();//自旋,没有抢到锁的线程
            } catch (InterruptedException e) {
     
                e.printStackTrace();
            }

        }

​ Redis实现分布式锁,使用Redis中的setnx来实现分布式锁,在Redis中的setn来实现,setnx保存同一个key的时候,成功返回0,失败返回1,即抢到锁的线程,没有抢到锁的线程,实现自旋(即在此执行抢锁的动作)。

你可能感兴趣的:(Redis的分布式锁实现原理)