0
点赞
收藏
分享

微信扫一扫

redis缓存-分布式锁原理与使用

  • 本地锁 只能锁定自己的服务 image.png

  • SET key value [EX seconds] [PX milliseconds] [NX|XX] http://www.redis.cn/commands/set.html

public Map<String, List<Catelog2Vo>> getCatalogJsonWithRedisLock() {
    //redis 分布原则 加锁保证原子性,解锁保证原子性
    //1.占用分布式锁 去redis站坑setIfAbsent = SET NX,随便set K V数值,之后返回一个结果 =>2.设置过期时间和枷锁是同步的原子的
    String uuid = UUID.randomUUID().toString(); //设置v值是uuid
    Boolean lock = redisTemplate.opsForValue().setIfAbsent("lock", uuid, 30, TimeUnit.SECONDS);
    if (lock) {
        System.out.println("获取分布式锁成功...");
        //1.1加锁成功 执行业务
        //2.设置期时间 以免造成死锁 设置 30秒自动删除 ;设置过期时间和枷锁是同步的原子的
        Map<String, List<Catelog2Vo>> dataFromDb;
        try {
            //redisTemplate.expire("lock",30,TimeUnit.SECONDS);
            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 = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("lock"), uuid);
        }
        //3.删除 不是自己锁 获取数值对比+对比删除成功=原子操作 lua 解锁操作
        /**
         * String lockValue = redisTemplate.opsForValue().get("lock"); //查到锁的数值
         *   //2.1查看放的uuid和lockValue一致 说明 锁是自己加的 在执行删除自己 锁
         *   if (uuid.equals(lockValue)){
         *     //1.2执行成功之后 解锁/删除锁 共其他使用
         *     redisTemplate.delete("lock");
         *     }
         */
        return dataFromDb;
    } else {
        //枷锁失败。。在重置,把自己在调一遍
        try {
            Thread.sleep(200);
        }catch (Exception e){
        }
        System.out.println("获取分布式锁失败 重试...");
        return getCatalogJsonWithRedisLock();//自旋方式

    }
}

导入pom

<!--用redisson作为分布式锁,分布式对象 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>

配置MyRedissonConfig

@Configuration
public class MyRedissonConfig {
    /**
     * 对于redisson配置是RedissonClient操作的
     *
     * @return
     * @throws IOException
     */
    @Bean(destroyMethod = "shutdown")
    RedissonClient redisson() throws IOException {
        //创建配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
        //config.useClusterServers() 集群模式
        //        .addNodeAddress("127.0.0.1:7004", "127.0.0.1:7001");
        //依据Config创建RedissonClient
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }
}

测试加锁 解锁

public String hello() {
    //1.获取一把锁 只要锁 名字一样就是用一把锁
    RLock lock = redisson.getLock("my_lock");
    //2.加锁
    lock.lock(30, TimeUnit.SECONDS);
    //lock.lock(); //堵塞式等待
    try {
        //打印线程好  如果服务中断 有个看门狗自动续期
        System.out.println("加锁成功执行业务"+Thread.currentThread().getId());
        Thread.sleep(30000);
    } catch (InterruptedException e) {
    } finally {
        //3.解锁
        System.out.println("解锁成功执行业务"+Thread.currentThread().getId());
        lock.unlock();
    }
    return "hello";
}

//2.加锁 锁的自动续期,如果业务超长,运行期间自动给锁续上行的时间,不用担心业务时间长 锁自动过期被删掉 // 加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认在30s以后自动删除。

  • 10秒自动解锁,自动解锁时间-定要大于业务的执行时间。在锁时间到了以后,不会自动续期。
  • 如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是我们指定的时间
  • 如果我们未指定锁的超时时间,就使用30 * 1000 [LockWatchdogTimeout看门狗的默认时间] ;
  • 只要占锁成功,就会启动一个定时任务[重新给锁设置过期时间,新的过期时间就是看[ ]狗的默认时间]
  • internallockLeaseTime [看门狗时间] / 3, 10s 规则用 lock.lock(30, TimeUnit.SECONDS); 加锁时间>=解锁时间

读写锁测试

  • 保证一 定能读到最新数据,修改期间,写锁是一 个排他锁(互斥锁,独享锁)。读锁是一 一个共享锁
  • 与锁役释放读就必须寺待
  • 读+读 相当于无锁,并发读,只会在redis中记录好,所有当前的读锁。他们都会同时加锁成功
  • 写+读:等待写锁释放
  • 写+写:阻塞方式
  • 读+写:有读锁。写也需要等待。
  • 只要有写的存在,都必须等待
@ResponseBody
@GetMapping("/write") //读写锁
public String writeValue() {
    String s = "";
    RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");//加写锁
    RLock rLock = lock.writeLock();
    try {
        //改数据加写锁,读数据加读锁
        rLock.lock();
        s = UUID.randomUUID().toString();
        redisTemplate.opsForValue().set("writeValue", s);
        Thread.sleep(30000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        rLock.unlock();
    }
    return s;
}

@ResponseBody
@GetMapping("/read") //读写锁
public String readValue() {
    String s = "";
    RReadWriteLock lock = redisson.getReadWriteLock("rw-lock");
    RLock rLock = lock.readLock(); //加读锁
    //执行业务代码
    rLock.lock();
    try {
        s = redisTemplate.opsForValue().get("writeValue");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        rLock.unlock();
    }
    return s;
}

Redisson-信号量测试

车库停车 信号量测试 限流使用

@GetMapping("park")
@ResponseBody
public String park() throws InterruptedException {
    //使用信号量命名
    RSemaphore park = redisson.getSemaphore("park");
    //获取一个信号 获取一个数值 占一个车位  获取失败会一直等待
    boolean b = park.tryAcquire(); //尝试获取 能否取到数值 不行就false 走
    if (b){
        //执行代码
    }else {
        return "error";
    }
    park.acquire();
    return "ok停车+1";
}

@GetMapping("go")
@ResponseBody
public String go() throws InterruptedException {
    //使用信号量命名
    RSemaphore park = redisson.getSemaphore("park");
    //释放信号
    park.release();
    return "ok 开走-1";
}

Redisson-闭锁测试

分布式闭锁 放假锁门 等到全部走完 才关锁

@GetMapping("/lockDoor")
@ResponseBody
public  String lockDoor() throws InterruptedException {
    RCountDownLatch doot = redisson.getCountDownLatch("door");
    doot.trySetCount(5);//等待5个
    doot.await();//等待闭锁都完成
    return "放假了";
}

@GetMapping("/gogo/{id}")//接收走的id
@ResponseBody
public  String gogo(@PathVariable("id") Long id){
    RCountDownLatch doot = redisson.getCountDownLatch("door");
    doot.countDown();//等价于计数减一
    return id+"走了";
}

修正之前方法

public Map<String, List<Catelog2Vo>> getCatalogJsonWithRedissonLock() {
    //去占锁
    RLock lock = redisson.getLock("catalogJson-lock");
    //占锁之后 加锁
    lock.lock();
    Map<String, List<Catelog2Vo>> dataFromDb;
    try {
        dataFromDb = getDataFromDb();
    } finally {
        //解锁
        lock.unlock();
    }
    return dataFromDb;
}

缓存-分布式锁-缓存一致性解决

image.png image.png image.png image.png

举报

相关推荐

0 条评论