Redisson文档

Redisson文档

redisson文档


:::info
redisson作为分布式锁等功能的框架,内部的所有锁机制,都是原子操作。看门狗机制保证不会出现死锁的情况
:::

1、引入依赖

<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson</artifactId>
    <version>3.12.0</version>
</dependency>

2、配置redisson

@Configuration
public class MyRedissonConfig {

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redisson(){
//        1、创建配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");
//        根据Config创建出RedissonClient
        return Redisson.create(config);
    }
}

3、lock锁机制

  • 获取一把锁,只需要锁的名字一样就是同一把锁
RLock myLock = redisson.getLock("my_lock");
  • 加锁

myLock.lock(); 阻塞式等待
1)、锁的自动续期,如果业务时间超长,运行时间自动给锁续上新的30秒。不用担心锁过期之后自动删除
2)、加锁的业务只要运行完,就不会给当前锁续期,即使不手动释放锁,也会在默认的30秒内自动删除
myLock.lock(10 , TimeUtil.SECONDS); 10秒之后自动过期,自动解锁时间一定要大于业务执行时间

  • 问题:lock.lock(10,TimeUtil.SECONDS); 在锁时间到了之后,不会自动续期

1)如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时时间就是我们指定的时间
2)如果我们未指定超时时间,就使用默认的 30*1000【LockWatchdogTimeout 看门狗的默认时间】;

只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】

internalLockLeaseTime【看门狗时间】 / 3

:::info
推荐使用myLock.lock(10 , TimeUtil.SECONDS) 自定义锁失效时间,之后手动解锁 myLock.unlock();
:::

4、读写锁

:::info
读写锁能保证一定能读到最新的数据,修改期间,写锁是排他锁(互斥锁,独享锁),读锁是一个共享锁
:::

@GetMapping("/write")
@ResponseBody
public String writeValue(){
    String s = UUID.randomUUID().toString();
    //        获取读写锁
    RReadWriteLock lock = redisson.getReadWriteLock("wr-lock");
    RLock rLock = lock.writeLock();
    try {
        rLock.lock();
        Thread.sleep(30000);
        redisTemplate.opsForValue().set("uuid", s);
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        rLock.unlock();
    }
    return s;
}
@GetMapping("/read")
@ResponseBody
public String readValue(){
    //        获取读写锁
    RReadWriteLock lock = redisson.getReadWriteLock("wr-lock");
    RLock rLock = lock.readLock();
    String uuid = "";
    try {
        rLock.lock();
        uuid = redisTemplate.opsForValue().get("uuid");
    } catch (Exception e) {
        e.printStackTrace();
    } finally {
        rLock.unlock();
    }
    return uuid;
}
  • 读 + 读 :相当于无锁,并发读只会在redis中记录好当前的读锁,他们都会枷锁成功
  • 写 + 读 :等待写锁释放
  • _写 + 写 : 阻塞方式,等待写锁释放 _
  • 读 + 写 :阻塞方式,有读锁,写锁也会等待

总结:只要有写锁的存在,都必须等待

5、闭锁

:::info
只有所有要执行的业务都执行完之后,才会去执行最终业务
:::

@GetMapping("/lockDoor")
@ResponseBody
public String lockDoor(){
    RCountDownLatch lockDoor = redisson.getCountDownLatch("lockDoor");
    lockDoor.trySetCount(5);
    try {
        lockDoor.await();
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return "锁门了";
}
@GetMapping("/gohome/{id}")
@ResponseBody
public String gohome(@PathVariable("id") Long id){
    RCountDownLatch lockDoor = redisson.getCountDownLatch("lockDoor");
    try {
        lockDoor.countDown();
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "放学了"+id;
}

6、信号量

:::info
可以用来做限流操作
:::

@GetMapping("/park")
@ResponseBody
public String park(){

    RSemaphore park = redisson.getSemaphore("park");
    try {
        park.acquire();//获取一个信号
    } catch (InterruptedException e) {
        throw new RuntimeException(e);
    }
    return "停";
}
@GetMapping("/go")
@ResponseBody
public String go(){

    RSemaphore park = redisson.getSemaphore("park");
    try {
        park.release();//获取一个信号
    } catch (Exception e) {
        e.printStackTrace();
    }
    return "走";
}

acquire:阻塞时等待
tryAcquire:非阻塞式等待,如果获取到则返回true,否则返回false

7、缓存一致性

  • 缓存数据一致性—双写模式

Redisson文档_第1张图片

  • 缓存数据一致性—失效模式

Redisson文档_第2张图片

:::info
缓存数据一致性—解决方案
:::

  • 无论是双写模式还是失效模式,都会导致缓存的不一致问题。即多个实例同时更新会出事。怎么办?
    • 1、如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加 上过期时间,每隔一段时间触发读的主动更新即可
    • 2、如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式。
    • 3、缓存数据+过期时间也足够解决大部分业务对于缓存的要求。
    • 4、通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心 脏数据,允许临时脏数据可忽略);
  • 总结
    • 我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保 证每天拿到当前最新数据即可。
    • 我们不应该过度设计,增加系统的复杂性
    • 遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。

canal-----解决数据一致性

Redisson文档_第3张图片

你可能感兴趣的:(java,java,redis,分布式)