springboot下Redission实现redis分布式锁

直接使用redisson实现分布式锁

加锁原理
在这里插入图片描述

  • 线程一去获取锁,获取成功,执行lua脚本,保存数据到redis数据库。线程二去获取锁,获取失败,
    一直通过while循环尝试获取锁。获取成功后,执行lua脚本,保存数据到redis数据库。
  • Watch dog所起到的作用就是当锁的有效时间要到了当业务逻辑却还没有执行完成时,延长锁的有效时间。
    注:正常这个看门狗线程是不启动的,还有就是这个看门狗启动后对整体性能也会有一定影响,所以不建议开启看门狗。
  • 将复杂的业务逻辑封装在lua脚本中发送给redis,且redis是原子性的,这样就保证了这段逻辑的原子性。

Redisson可以实现可重入加锁机制

在这里插入图片描述

  • 线程二在已经持有锁的情况下再进去,就不需要改线程ID,只需改一下value值即可。这里有点像偏向锁的加锁过程:当检查锁标志位成功的时候,会通过CAS的操作,将Mark
    Word中的线程ID改为自己的线程ID,并将偏向锁位置为1。如果下一次还是线程二进入同步区,就不需要执行这一步

在Redis哨兵模式下,当线程一给master节点写入redission锁,会异步复制给slave节点。如果此时master节点发生故障宕机,就会发生主备切换,slave节点变成了master节点。此时线程二也可以给新的master节点写入redission锁。这样就会产生在同一时刻能有多个客户端对同一个分布式锁加锁,这样就可能会导致脏数据的产生。

上一波依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.8.2</version>
</dependency>
package com.fchan.redission.util;

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissionUtil {
    
    

    @Bean
    public RedissonClient redissionClient() {
    
    
        Config config = new Config();

        //单机模式
        config.useSingleServer()
                .setAddress("redis://192.168.56.101:36379")
                .setPassword("111")
                .setDatabase(1);

        //集群模式
        /*config.useClusterServers()
                .addNodeAddress("redis://192.168.56.101:36379")
                .addNodeAddress("redis://192.168.56.102:36379")
                .addNodeAddress("redis://192.168.56.103:36379")
                .setPassword("1111111")
                .setScanInterval(5000);*/

        //哨兵模式
        /*config.useSentinelServers().addSentinelAddress("redis://ip1:port1",
                "redis://ip2:port2",
                "redis://ip3:port3")
                .setMasterName("mymaster")
                .setPassword("password")
                .setDatabase(0);*/

        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }


}

使用redisson的并发锁

package com.fchan.redission.controller;

import lombok.extern.slf4j.Slf4j;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.concurrent.TimeUnit;

@RestController
@RequestMapping("api/redisson")
@Slf4j
public class RedissonController {
    
    


    @Autowired
    private RedissonClient redissonClient;

    public static Integer count = 0;


    @PostMapping("test")
    public String testRedisson(){
    
    

        RLock lock = redissonClient.getLock("myLock");
        try {
    
    


            log.info("开始加锁");
            count ++;
            log.info("count ++ 之后的值 {}",count);

            //lock.lock();    //简单的获取锁

            // 获取锁最多等待500ms,10s后key过期自动释放锁
            boolean getLock = lock.tryLock(500,10000, TimeUnit.MILLISECONDS);
            if (!getLock) {
    
    
                log.info("当前线程:[{}]没有获得锁", Thread.currentThread().getName());
                return "failed";
            }

        } catch (Exception e) {
    
    
            e.printStackTrace();
        } finally {
    
    
            //lock.isHeldByCurrentThread() 判断当前线程是否持有锁
            if(null != lock && lock.isHeldByCurrentThread()){
    
    
                lock.unlock();
            }
        }
        return "success";
    }

}

锁过期时间的设置

  • 这个问题的场景是,假设设置失效时间10秒,如果由于某些原因导致10秒还没执行完任务,这时候锁自动失效,导致其他线程也会拿到锁。

集群模式下主从切换导致的锁丢失

  • 这个问题的场景 在Redis的master节点上拿到了锁; 但是这个加锁的key还没有同步到slave节点;
    master故障,发生故障转移,slave节点升级为master节点; 导致锁丢失。

猜你喜欢

转载自blog.csdn.net/weixin_43944305/article/details/112256180