本站消息

站长简介/公众号

  出租广告位,需要合作请联系站长


+关注
已关注

分类  

暂无分类

标签  

暂无标签

日期归档  

2024-11(1)

缓存篇-Redisson的使用

发布于2021-05-29 19:48     阅读(1229)     评论(0)     点赞(28)     收藏(1)



前言

使用redisson进行redis客户端操作


一、Redisson是什么?

Redisson是一个在Redis的基础上实现的Java驻内存数据网络(In-Memory Data Grid)。不仅提供了一些列的分布式java常用对象,还提供了许多分布式服务。
Redission提供了Redis最简单和最便捷的方法。Redisson的宗旨是促进使用者对Redis的关注分离,集中精力在业务处理上。

二、使用Redisson

1.引入pom依赖

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

2.配置注入对象

@Configuration
public class MyRedisConfig {

  @Value("${spring.redis.host}")
   private  String redisIp;


   @Value("${spring.redis.port}")
   private  String redisPort;

   //注入RedissonClient
   @Bean(destroyMethod = "shutdown")
   RedissonClient redisson(){
       //编写配置
       Config config = new Config();
       //单节点模式
       config.useSingleServer().setAddress("redis://"+redisIp +":"+redisPort);
       return Redisson.create(config);
   }
}

三、使用Redisson实现可重入锁(Reentrant Lock)

1.原因

如果A调用B。A、B都需要同一把锁,此时使用可重入锁(Reetrant Lock)就能实现可重入,A调用B。否则如果是不可重入,调用B的前提是A释放锁,A释放锁的前提是调用B,此时就形成死锁。

2.redisson使用

@ResponseBody
@RequestMapping("/hello")
public String hello(){
    //获取一把锁 只要锁的名字一样就说是同一把锁
    RLock lock = redisson.getLock("my-lock");
    lock.lock();
    try {
        System.out.println("走业务代码");
    }finally {
        lock.unlock();
    }
    return "hello";
}

3.redisson看门狗机制

在这里插入图片描述

  • 锁的续期:如果没有锁的续期,存储分布式锁的redisson节点宕机,正好这个锁处于锁住状态时,这个锁就处于死锁状态。为了避免这种情况,redisson内部提供了一种监控锁的看门狗机制。
  • 看门狗的作用:在redisson实例被关闭之前,不断延长锁的有效时间。默认情况看门狗检查锁的时间是30秒。

4.redisson看门狗源码分析

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

  • 如果传递了锁的超时时间,就执行脚本,进行占锁;
  • 如果没传递锁时间,使用看门狗的时间,占锁。如果返回占锁成功future,调用future.onComplete();
    没异常的话调用scheduleExpirationRenewal(threadId);
    重新设置过期时间,定时任务;

看门狗的原理是定时任务:重新给锁设置过期时间,新的过期时间就是看门狗的默认时间;锁时间/3是定时任务周期;

四、使用Redisson实现读写锁(ReadWrite Lock)

分布式可重入读写锁允许同时又多个读锁和写锁处于加锁状态。
在这里插入图片描述
读时处于无锁状态。

五、使用Redisson实现信号量(Semaphore)

@GetMapping("/park")
@ResponseBody
public String park() {
    RSemaphore park = redisson.getSemaphore("park");
    try {
        park.acquire(2);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "park";
}

@GetMapping("/go")
@ResponseBody
public String go() {
    RSemaphore park = redisson.getSemaphore("park");
    park.release(2);
    return "go";
}

信号量可以做限流等应用

六、使用Redisson实现分布式闭锁(CountDownLatch)

在这里插入图片描述

七、缓存和数据库一致性解决方案

  • 双写模式。写完数据库后,写缓存。
  • 失效模式。写完数据库后,删缓存。

1.出现的问题

双写模式:
并发时,2号线程进入,写完DB后写缓存。而此时1号线程还没有写缓存,造成短时间内缓存有脏数据。
在这里插入图片描述
失效模式:
并发时,
1号线程先写数据库,删缓存,
2号线程此时依然还在写数据库,
但是3号线程读缓存发现没有缓存,此时去读取数据库,读数据库完成之后,2号线程才写完
在3号线程写缓存之前,2号线程写完数据库,也删完了缓存,
此时3号线程更新缓存。但是更新的依然是1号线程写的数据,2号线程写的数据没有加入到缓存。
在这里插入图片描述

2.解决方案

  • 如果是用户纬度数据(订单数据、用户数据),这种并发几率非常小,不用考虑这个问题,缓存数据加上过期时间,每隔一段时间触发读的主动更新即可。

  • 如果是菜单,商品介绍等基础数据,也可以去使用canal订阅binlog的方式
    缓存数据+过期时间也足够解决大部分业务对于缓存的要求。
    在这里插入图片描述

  • 通过加锁保证并发读写,写写的时候按顺序排好队。读读无所谓。所以适合使用读写锁。(业务不关心脏数据,允许临时脏数据可忽略);

3.总结

  • 我们能放入缓存的数据本就不应该是实时性、一致性要求超高的。所以缓存数据的时候加上过期时间,保证每天拿到当前最新数据即可。

  • 我们不应该过度设计,增加系统的复杂性

  • 遇到实时性、一致性要求高的数据,就应该查数据库,即使慢点。

原文链接:https://blog.csdn.net/Simon_09010817/article/details/117327251



所属网站分类: 技术文章 > 博客

作者:码神

链接:http://www.javaheidong.com/blog/article/207244/f5d8e8cb5fe0da87e54a/

来源:java黑洞网

任何形式的转载都请注明出处,如有侵权 一经发现 必将追究其法律责任

28 0
收藏该文
已收藏

评论内容:(最多支持255个字符)