缓存与分布式锁

文章介绍了如何在SpringBoot项目中集成Redis,通过排除Lettuce依赖并引入Jedis来操作Redis。同时,讨论了缓存管理中的问题,如缓存穿透、缓存雪崩和缓存击穿,并提出了解决策略。此外,文章还涉及了分布式锁的实现,使用Redisson创建分布式锁,以及可重入锁在防止死锁中的重要性。
摘要由CSDN通过智能技术生成

使用
在这里插入图片描述

Redis

boot整合Redis

  1. docker安装Redis,参考Docker安装mysql和redis详细介绍
  2. 引入依赖,排除lettuce,使用jedis
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
    <exclusions>
        <exclusion>
            <groupId>io.lettuce</groupId>
            <artifactId>lettuce-core</artifactId>
        </exclusion>
    </exclusions>
</dependency>

<dependency>
    <groupId>redis.clients</groupId>
    <artifactId>jedis</artifactId>
</dependency>
  1. application.yml
spring:
  redis:
    host: 118.31.188.33
    port: 6379
  1. 使用SpringBoot自动配置好的StringRedisTemplate
@RunWith(SpringRunner.class)


	@Autowired
    private StringRedisTemplate stringRedisTemplate;
    
	@Test
    public void testStringRedisTemplate() {
        ValueOperations<String, String> ops = stringRedisTemplate.opsForValue();

        //保存
        ops.set("hello","world_" + UUID.randomUUID().toString());

        //查询
        String hello = ops.get("hello");
        System.out.println("之前保存的数据:"+hello);
    }

tips

  1. 一般往缓存中存json,json之间的转换采用alibaba的fastjson
// 转换为json
String s = JSON.toJSONString();

// json转化为指定对象
JSON.parseObject()

高并发下的缓存失效问题

  1. 缓存穿透:查询一个缓存和数据库中都不存在的数据
    解决:空结果缓存,就是从数据库查询结果为空时,往缓存存放0或者其他非null的值,查询到数据时,就把数据放入缓存
    在这里插入图片描述

  2. 缓存雪崩:缓存在某一时刻集体失效
    解决:给每个缓存数据设置不同的过期时间
    xxx.opsForValue.set()可以设置过期时间
    在这里插入图片描述

  3. 缓存击穿:某一热点数据过期失效,失效时大量请求进来,刚好落到数据库上
    解决:加锁
    在这里插入图片描述

本地锁只能锁住当前进程(一个进程相当于一个服务)
本地锁使用注意事项
1.线程拿到锁后第一件事应该是判断缓存中有没有想要的数据,而不是直接查询数据库。
2.锁的时序问题
在这里插入图片描述
解决方法:把查询数据库操作和放入缓存操作放在同一把锁内执行

分布式锁推演
一、
在这里插入图片描述
来到阶段二
二、
在这里插入图片描述
来到阶段三
三、
在这里插入图片描述
解决:
在这里插入图片描述
四、
在这里插入图片描述
五、
在这里插入图片描述

复制一个服务 --server.port=10001

分布式锁

redisson

  1. 导依赖
    org.redisson.redisson.3.12.0

  2. config/MyRedissonConfig
    参照官方文档12
    在这里插入图片描述
    在这里插入图片描述

@Configuration
public class MyRedissonConfig {

    /**
     * 所有对Redisson的使用都是通过RedissonClient
     */
    @Bean(destroyMethod="shutdown")
    public RedissonClient redisson() throws IOException {
        //1、创建配置
        Config config = new Config();
        config.useSingleServer().setAddress("redis://192.168.56.10:6379");

        //2、根据Config创建出RedissonClient实例
        //Redis url should start with redis:// or rediss://
        RedissonClient redissonClient = Redisson.create(config);
        return redissonClient;
    }

}
  1. 测试
@Autowired
RedissonClient redissonClient;
@Test
public void testRedisson() {
    System.out.println(redissonClient);
}

可重入锁
假设有方法A和方法B,方法A调用方法B,方A和方法B同一把锁,方法A调用方法B时,B看A已经加了同一把锁了,就把锁直接拿来用,这样B就可以正常执行,执行完A释放锁

不可重入锁
A和B都想加同一把锁,A调用B,一进到A,A持有锁,调用到B时,B需要等A释放锁才能抢到锁,B在等A,A想调B,造成死锁

看门狗

@ResponseBody
@GetMapping(value = "/hello")
public String hello() {

    //1、获取一把锁,只要锁的名字一样,就是同一把锁
    RLock myLock = redisson.getLock("my-lock");

    //2、加锁
    myLock.lock();      //阻塞式等待。默认加的锁都是30s
    //1)、锁的自动续期,如果业务超长,运行期间自动锁上新的30s。不用担心业务时间长,锁自动过期被删掉
    //2)、加锁的业务只要运行完成,就不会给当前锁续期,即使不手动解锁,锁默认会在30s内自动过期,不会产生死锁问题
    // myLock.lock(10,TimeUnit.SECONDS);   //10秒钟自动解锁,自动解锁时间一定要大于业务执行时间
    //问题:在锁时间到了以后,不会自动续期
    //1、如果我们传递了锁的超时时间,就发送给redis执行脚本,进行占锁,默认超时就是 我们制定的时间
    //2、如果我们指定锁的超时时间,就使用 lockWatchdogTimeout = 30 * 1000 【看门狗默认时间】
    //只要占锁成功,就会启动一个定时任务【重新给锁设置过期时间,新的过期时间就是看门狗的默认时间】,每隔10秒都会自动的再次续期,续成30秒
    // internalLockLeaseTime 【看门狗时间】 / 3, 10s
    try {
        System.out.println("加锁成功,执行业务..." + Thread.currentThread().getId());
        try { TimeUnit.SECONDS.sleep(20); } catch (InterruptedException e) { e.printStackTrace(); }
    } catch (Exception ex) {
        ex.printStackTrace();
    } finally {
        //3、解锁  假设解锁代码没有运行,Redisson会不会出现死锁
        System.out.println("释放锁..." + Thread.currentThread().getId());
        myLock.unlock();
    }

    return "hello";
}

因此,所有所都应该设计成可重入锁,避免死锁问题

只加synchronized,==》线程排队等锁,有多少个请求就会查询几次数据库
所以在锁的开始,需要判断缓存中是否已经有想要的数据了 这样最终只会查询一次数据库

上述只能用于单体应用,分布式应用有很多服务,每个服务对应一个实例,一个进程 有几个实例就查询几次数据库

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值