专栏
天翼云开发者社区

Java:Redis分布式缓存

2022-06-30 19:02:22 32阅读

1、Redis作为缓存

Redis是一款内存高速缓存数据库;

数据模型为:key - value,非关系型数据库使用的存储数据的格式;

 

可持久化:将内存数据在写入之后按照一定格式存储在磁盘文件中,宕机、断电后可以重启redis时读取磁盘中文件恢复缓存数据;

 

分布式:当前任务被多个节点切分处理,叫做分布式处理一个任务。单个服务器内存,磁盘空间有限,无法处理海量的缓存数据,必须支持分布式的结构;

 

SpringBoot 2.x引入Redis缓存

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

样例:采用Redis缓存。

Map<Long, List<Catelog2Vo>> listMap = null;

    String category= (String) redisUtils.get("Category");

    if(StringUtils.isEmpty(category)){

        listMap = getCategoryJsonFromDb();

        String s = JSON.toJSONString(listMap);

        redisUtils.set("Category", s);

    }else {

        listMap = JSON.parseObject(category, new TypeReference<Map<Long, List<Catelog2Vo>>>(){});

    }

 

    return listMap;

总结:当前高高并发中会造成缓存穿透

2、高并发-缓存穿透

缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中,将查询数据库,但是数据库也无此记录。

我们没有将查询的null写入缓存,导致不存在的数据每次请求数据存储层,失去缓存的意义。

 

风险:

利用不存在的数据进行攻击,数据库压力瞬时增大,最终导致崩溃

 

解决方法:

null结果缓存,并加入短暂过期时间

 

对上诉样例改造:

if(listMap != null){

    String s = JSON.toJSONString(listMap);

    redisUtils.set("CatalogJson", s, 24*60*60);

}

else{

    redisUtils.set("CatalogJson", null, 24*60*60);

 

}

if(listMap != null){

    String s = JSON.toJSONString(listMap);

    redisUtils.set("CatalogJson", s, 24*60*60);

}

else{

    redisUtils.set("CatalogJson", null, 24*60*60);

}

总结:通过设置过期时间和缓存null来解决缓存穿透问题,但是还是会遇到高并发导致服务器雪崩问题

3、高并发-缓存雪崩

缓存雪崩:指我们设置的缓存时key采用相同的过期时间,导致缓存在某一时刻同时失效,请求全部转换到存储层DB,存储层瞬间压力过重导致雪崩

 

解决方法:原有的失效时间基础上增加一个随机值,比如1-10分钟随机,这样每一个缓存的过期时间的重复率就会降低,就能大概率避免集体失效的事件。

 

对上诉代码改造:

int randomTime = (int)(1+Math.random()*(10-1+1));//随机数要自己改造

if(listMap != null){

    String s = JSON.toJSONString(listMap);

    redisUtils.set("CatalogJson", s, 24*60*60 + randomTime);

}

else{

    redisUtils.set("CatalogJson", null, 24*60*60 + randomTime);

 

}

总结:解决了缓存穿透和缓存雪崩问题,但是还是存在某个时间点出现的缓存击穿问题

4、高并发-缓存击穿

缓存击穿:对于一些设置过期时间的key,如何这些key可能会在某些时间点被高并发访问,是一个非常热点的数据,如果这个key在大量请求同时进来钱正好失效,那么所有的key数据查询到落到存储层DB。我们称为缓存击穿。

解决方法: 加锁,大量并发只让一个去查,其他人等待,查到以后释放锁。其他人获取到锁,先查询缓存,就有数据了,不用去存储层查询。

 

a、本地锁

本地锁只锁当前进程,无法完全锁住高并发。

synchronized (this){

//保证锁的原子性

}

在这里插入图片描述

b、分布式锁

加锁保证原子性:

String uuid  = UUID.randomUUID().toString();

boolean lock = redisTemplate.opsForValue().setIfAbsent("category_lock", uuid, 120, TimeUnit.SECONDS);

删锁保证原子性:采用lua脚本

String script ="if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

 

Long lockInt = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("category_lock"), uuid);

在这里插入图片描述

c、Redisson分布式锁

Redission 为 Redis 官网分布式解决方案

官网: https://redisson.org/

github: https://github.com/redisson/redisson#quick-start

 

pom.xml引入redisson maven关联

 <dependency>

     <groupId>org.redisson</groupId>

     <artifactId>redisson</artifactId>

     <version>3.16.0</version>

 </dependency>

添加redisson config

@Configuration

public class MyRedissonConfig {

    @Bean(destroyMethod = "shutdown")

    RedissonClient redisson() throws IOException {

        Config config = new Config();

        config.useSingleServer().setAddress("redis://127.0.0.1:6379");

        return Redisson.create(config);

    }

 

}

具体如何使用可以参考官网。

d、SpringCache

SpringCache 常用注解为:@Cacheable、@CachePut、@CacheEvit

 

@Cacheable 的作用:适用于获取数据,如果缓存中有数据,不在调用注解方法;

 

@CachePut 的作用:适合于插入数据和更新数据。一定会调用真实方法,再将方法返回值保存到缓存;

 

@CachEvict 的作用:适合于删除数据。

 

引入pom.xml

 <dependency>

     <groupId>org.springframework.boot</groupId>

     <artifactId>spring-boot-starter-cache</artifactId>

 

 </dependency>

解决缓存穿透方式:添加允许缓存空值的配置

 

spring.cache.redis.cache-null-values=true

解决缓存雪崩方式:添加同步锁

 

@Cacheable(value = {"category"},key = "#root.method.name", sync = true)

解决缓存击穿方式:加上过期时间和随机时间配置

 

spring.cache.redis.time-to-live=360000

总结:读多写少,即时性和一致性要求不高,使用springcache就足够了。写模式下可以添加缓存过期时间来满足。

————————————————

版权声明:本文为CSDN博主「陌晓风」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

 

原文链接:https://blog.csdn.net/lishangke/article/details/119580909

 

  • 0
  • 0
  • 0
0 评论
0/1000
评论(0) 发表评论
天翼云文档找茬小助手

天翼云文档找茬小助手

41 篇文章 7 粉丝
关注

Java:Redis分布式缓存

2022-06-30 19:02:22 32阅读

1、Redis作为缓存

Redis是一款内存高速缓存数据库;

数据模型为:key - value,非关系型数据库使用的存储数据的格式;

 

可持久化:将内存数据在写入之后按照一定格式存储在磁盘文件中,宕机、断电后可以重启redis时读取磁盘中文件恢复缓存数据;

 

分布式:当前任务被多个节点切分处理,叫做分布式处理一个任务。单个服务器内存,磁盘空间有限,无法处理海量的缓存数据,必须支持分布式的结构;

 

SpringBoot 2.x引入Redis缓存

<dependency>

<groupId>org.springframework.boot</groupId>

<artifactId>spring-boot-starter-data-redis</artifactId>

</dependency>

样例:采用Redis缓存。

Map<Long, List<Catelog2Vo>> listMap = null;

    String category= (String) redisUtils.get("Category");

    if(StringUtils.isEmpty(category)){

        listMap = getCategoryJsonFromDb();

        String s = JSON.toJSONString(listMap);

        redisUtils.set("Category", s);

    }else {

        listMap = JSON.parseObject(category, new TypeReference<Map<Long, List<Catelog2Vo>>>(){});

    }

 

    return listMap;

总结:当前高高并发中会造成缓存穿透

2、高并发-缓存穿透

缓存穿透:指查询一个一定不存在的数据,由于缓存是不命中,将查询数据库,但是数据库也无此记录。

我们没有将查询的null写入缓存,导致不存在的数据每次请求数据存储层,失去缓存的意义。

 

风险:

利用不存在的数据进行攻击,数据库压力瞬时增大,最终导致崩溃

 

解决方法:

null结果缓存,并加入短暂过期时间

 

对上诉样例改造:

if(listMap != null){

    String s = JSON.toJSONString(listMap);

    redisUtils.set("CatalogJson", s, 24*60*60);

}

else{

    redisUtils.set("CatalogJson", null, 24*60*60);

 

}

if(listMap != null){

    String s = JSON.toJSONString(listMap);

    redisUtils.set("CatalogJson", s, 24*60*60);

}

else{

    redisUtils.set("CatalogJson", null, 24*60*60);

}

总结:通过设置过期时间和缓存null来解决缓存穿透问题,但是还是会遇到高并发导致服务器雪崩问题

3、高并发-缓存雪崩

缓存雪崩:指我们设置的缓存时key采用相同的过期时间,导致缓存在某一时刻同时失效,请求全部转换到存储层DB,存储层瞬间压力过重导致雪崩

 

解决方法:原有的失效时间基础上增加一个随机值,比如1-10分钟随机,这样每一个缓存的过期时间的重复率就会降低,就能大概率避免集体失效的事件。

 

对上诉代码改造:

int randomTime = (int)(1+Math.random()*(10-1+1));//随机数要自己改造

if(listMap != null){

    String s = JSON.toJSONString(listMap);

    redisUtils.set("CatalogJson", s, 24*60*60 + randomTime);

}

else{

    redisUtils.set("CatalogJson", null, 24*60*60 + randomTime);

 

}

总结:解决了缓存穿透和缓存雪崩问题,但是还是存在某个时间点出现的缓存击穿问题

4、高并发-缓存击穿

缓存击穿:对于一些设置过期时间的key,如何这些key可能会在某些时间点被高并发访问,是一个非常热点的数据,如果这个key在大量请求同时进来钱正好失效,那么所有的key数据查询到落到存储层DB。我们称为缓存击穿。

解决方法: 加锁,大量并发只让一个去查,其他人等待,查到以后释放锁。其他人获取到锁,先查询缓存,就有数据了,不用去存储层查询。

 

a、本地锁

本地锁只锁当前进程,无法完全锁住高并发。

synchronized (this){

//保证锁的原子性

}

在这里插入图片描述

b、分布式锁

加锁保证原子性:

String uuid  = UUID.randomUUID().toString();

boolean lock = redisTemplate.opsForValue().setIfAbsent("category_lock", uuid, 120, TimeUnit.SECONDS);

删锁保证原子性:采用lua脚本

String script ="if redis.call('get',KEYS[1]) == ARGV[1] then return redis.call('del',KEYS[1]) else return 0 end";

 

Long lockInt = redisTemplate.execute(new DefaultRedisScript<Long>(script, Long.class), Arrays.asList("category_lock"), uuid);

在这里插入图片描述

c、Redisson分布式锁

Redission 为 Redis 官网分布式解决方案

官网: https://redisson.org/

github: https://github.com/redisson/redisson#quick-start

 

pom.xml引入redisson maven关联

 <dependency>

     <groupId>org.redisson</groupId>

     <artifactId>redisson</artifactId>

     <version>3.16.0</version>

 </dependency>

添加redisson config

@Configuration

public class MyRedissonConfig {

    @Bean(destroyMethod = "shutdown")

    RedissonClient redisson() throws IOException {

        Config config = new Config();

        config.useSingleServer().setAddress("redis://127.0.0.1:6379");

        return Redisson.create(config);

    }

 

}

具体如何使用可以参考官网。

d、SpringCache

SpringCache 常用注解为:@Cacheable、@CachePut、@CacheEvit

 

@Cacheable 的作用:适用于获取数据,如果缓存中有数据,不在调用注解方法;

 

@CachePut 的作用:适合于插入数据和更新数据。一定会调用真实方法,再将方法返回值保存到缓存;

 

@CachEvict 的作用:适合于删除数据。

 

引入pom.xml

 <dependency>

     <groupId>org.springframework.boot</groupId>

     <artifactId>spring-boot-starter-cache</artifactId>

 

 </dependency>

解决缓存穿透方式:添加允许缓存空值的配置

 

spring.cache.redis.cache-null-values=true

解决缓存雪崩方式:添加同步锁

 

@Cacheable(value = {"category"},key = "#root.method.name", sync = true)

解决缓存击穿方式:加上过期时间和随机时间配置

 

spring.cache.redis.time-to-live=360000

总结:读多写少,即时性和一致性要求不高,使用springcache就足够了。写模式下可以添加缓存过期时间来满足。

————————————————

版权声明:本文为CSDN博主「陌晓风」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

 

原文链接:https://blog.csdn.net/lishangke/article/details/119580909

 

文章来自专栏

云知识的搬运工

224 篇文章 7 订阅
0 评论
0/1000
评论(0) 发表评论
  • 0
    点赞
  • 0
    收藏
  • 0
    评论