springboot业务开发--springboot集成redis解决缓存雪崩穿透问题

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
简介: 该文介绍了缓存使用中可能出现的三个问题及解决方案:缓存穿透、缓存击穿和缓存雪崩。为防止缓存穿透,可校验请求数据并缓存空值;缓存击穿可采用限流、热点数据预加载或加锁策略;缓存雪崩则需避免同一时间大量缓存失效,可设置随机过期时间。文章还提及了Spring Boot中Redis缓存的配置,包括缓存null值、使用前缀和自定义过期时间,并提供了改造代码以实现缓存到期时间的个性化设置。

一、缓存使用的若干问题

1.1.缓存穿透

正常情况下,我们去查询数据大部分都是存在的。如果请求去查询一条压根儿数据库中根本就不存在的数据,也就是缓存和数据库都查询不到这条数据,但是请求每次都会打到数据库上面去,造成对后端数据库的强大压力。这种查询不存在数据的现象我们称为缓存穿透。(有可能会是某些不法份子的恶意行为,多线程打满去向服务查询不存在的数据)

解决办法

   做好查询请求的数据校验,治标不治本

   缓存空值,之所以会穿透缓存给压力到数据库,就是因为缓存层没有缓存null值。后文会说明在Spring Boot环境下如何配置

   使用redis BloomFilter(这个已经脱离了Spring Boot课程范围,了解即可或自行学习)

1.2.缓存击穿

在平常高并发的系统中,大量的请求同时查询一个 key 时,此时这个key正好失效了,就会导致大量的请求都打到数据库上面去。这种现象我们称为缓存击穿。

比如:鹿晗宣布恋情,导致微博瘫痪。就有可能是缓存击穿导致的,大家都去看这一个热点新闻,热点新闻的缓存如果超时失效了,就造成后端服务压力增大,服务器瘫痪。(当然这只是我猜的,举例而已)

解决办法

   可以通过准确的监控热点流量,及时的针对热点服务及缓存组件进行自动化的扩容。

   通过Hystrix或sentinel等服务限流工具,保证系统的可用性,拒绝掉一部分流量的访问。

   第三种方法就是加锁,SpringCache采用sync属性,只有一个线程去维护缓存,其他线程会被阻塞,直到缓存中更新该条目为止。也就是第一次查询只允许一个线程,等数据被缓存之后,才支持并发。

   @Cacheable(value = CACHE_OBJECT,key = "#id",sync=true)    

   public ArticleVO getArticle(Long id) {

1.3.缓存雪崩

同一时刻大量缓存失效,导致请求集中的全部打到数据库。比如:双十一零点搞活动,为了支撑这次活动,事先已经缓存好大量的数据。如果所有的数据全是缓存24小时,那24小时之后这些数据缓存将集中失效,最终结果就是11.12号服务崩溃。

解决办法

   可以通过准确的监控热点流量,及时的针对热点服务及缓存组件进行自动化的扩容。

   不同缓存的失效时间不能一致,同一种缓存的失效时间也尽量随机(最小值-->最大值)

二、redis 缓存配置

在 application.yml指定 spring.cache.type=redis。

   spring:

     cache:

       type: redis

       redis:

         cache-null-values: true   # 缓存null,防止缓存穿透

         use-key-prefix: true  # 是否使用缓存前缀

         key-prefix: boot-launch  # 缓存前缀,缓存按应用分类

         time-to-live:  3600  # 缓存到期时间,默认不主动删除永远不到期

其中值得注意的一点是,Spring Cache默认只支持全局对所有的缓存配置生效时间,不支持对缓存的生效时间分类配置,容易造成缓存雪崩。

三、自定义缓存到期时间

由于redis缓存设置的到期时间是统一的,没有办法根据缓存名称(value属性)分别设置缓存到期的时间,容易造成缓存雪崩。所以我们进行一个简单的改造。在改造之前我们先来看一下RedisCacheManager源码

RedisCacheManager构造函数包含三个参数

   RedisCacheWriter这个在之前的章节我们就配置过

   RedisCacheConfiguration defaultCacheConfiguration 这个是默认的全局配置,针对所有缓存

   Map<String, RedisCacheConfiguration> initialCacheConfigurations这个是针对某一种缓存的个性化配置,泛型String是缓存名称,泛型RedisCacheConfiguration是该缓存的个性化配置

理解了上面的源码,下面的改造代码就不难理解了。

   @Data

   @Configuration

   @ConfigurationProperties(prefix = "caching")  //application.yml配置前缀

   public class RedisConfig {

   

       //11.4章节代码,不是本节内容

       @Bean

       public RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {

           RedisTemplate redisTemplate = new RedisTemplate();

           redisTemplate.setConnectionFactory(redisConnectionFactory);

           Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer(Object.class);

   

           ObjectMapper objectMapper = new ObjectMapper();

           objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);

           objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

   

           jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

   

           //序列化重点在这四行代码

           redisTemplate.setKeySerializer(new StringRedisSerializer());

           redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);

           redisTemplate.setHashKeySerializer(new StringRedisSerializer());

           redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);

   

           redisTemplate.afterPropertiesSet();

           return redisTemplate;

       }

   

   

        //从这里开始改造

       //自定义redisCacheManager

       @Bean

       public RedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {

           RedisCacheWriter redisCacheWriter = RedisCacheWriter.nonLockingRedisCacheWriter(redisTemplate.getConnectionFactory());

   

           RedisCacheManager redisCacheManager = new RedisCacheManager(redisCacheWriter,

                   this.buildRedisCacheConfigurationWithTTL(redisTemplate,RedisCacheConfiguration.defaultCacheConfig().getTtl().getSeconds()),  //默认的redis缓存配置

                   this.getRedisCacheConfigurationMap(redisTemplate)); //针对每一个cache做个性化缓存配置

   

           return  redisCacheManager;

       }

   

       //配置注入,key是缓存名称,value是缓存有效期

       private Map<String,Long> ttlmap;  //lombok提供getset方法

   

       //根据ttlmap的属性装配结果,个性化RedisCacheConfiguration

       private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap(RedisTemplate redisTemplate) {

           Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();

   

           for(Map.Entry<String, Long> entry : ttlmap.entrySet()){

               String cacheName = entry.getKey();

               Long ttl = entry.getValue();

               redisCacheConfigurationMap.put(cacheName,this.buildRedisCacheConfigurationWithTTL(redisTemplate,ttl));

           }

   

           return redisCacheConfigurationMap;

       }

   

       //根据传参构建缓存配置

       private RedisCacheConfiguration buildRedisCacheConfigurationWithTTL(RedisTemplate redisTemplate,Long ttl){

           return  RedisCacheConfiguration.defaultCacheConfig()

                   .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(redisTemplate.getValueSerializer()))

                   .entryTtl(Duration.ofSeconds(ttl));

       }

   

   }

   

四、自定义配置实现缓存失效时间个性化

在 application.yml指定 缓存名称对应的缓存生效时间,单位为秒

   caching:

     ttlmap:

       article: 10

       xxx: 20

       yyy: 50


相关文章
|
1月前
|
XML 测试技术 API
利用C#开发ONVIF客户端和集成RTSP播放功能
利用C#开发ONVIF客户端和集成RTSP播放功能
1069 123
|
5月前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
3月前
|
缓存 数据库连接 数据库
缓存三剑客(穿透、击穿、雪崩)
缓存穿透指查询数据库和缓存中都不存在的数据,导致请求直接冲击数据库。解决方案包括缓存空对象和布隆过滤器。缓存击穿是大量请求访问同一个失效的热点数据,使数据库瞬间压力剧增,解决方法有提前预热、设置永不过期、加锁限流等。缓存雪崩是大量key同时失效,导致所有请求直达数据库,可通过引入随机过期时间缓解。三者分别对应单点爆破、全面崩塌等问题,需根据场景选择合适策略优化系统性能与稳定性。
245 0
|
3月前
|
数据采集 运维 DataWorks
DataWorks 千万级任务调度与全链路集成开发治理赋能智能驾驶技术突破
智能驾驶数据预处理面临数据孤岛、任务爆炸与开发运维一体化三大挑战。DataWorks提供一站式的解决方案,支持千万级任务调度、多源数据集成及全链路数据开发,助力智能驾驶模型数据处理与模型训练高效落地。
|
1月前
|
存储 缓存 NoSQL
Redis专题-实战篇二-商户查询缓存
本文介绍了缓存的基本概念、应用场景及实现方式,涵盖Redis缓存设计、缓存更新策略、缓存穿透问题及其解决方案。重点讲解了缓存空对象与布隆过滤器的使用,并通过代码示例演示了商铺查询的缓存优化实践。
125 1
Redis专题-实战篇二-商户查询缓存
|
16天前
|
缓存 负载均衡 监控
135_负载均衡:Redis缓存 - 提高缓存命中率的配置与最佳实践
在现代大型语言模型(LLM)部署架构中,缓存系统扮演着至关重要的角色。随着LLM应用规模的不断扩大和用户需求的持续增长,如何构建高效、可靠的缓存架构成为系统性能优化的核心挑战。Redis作为业界领先的内存数据库,因其高性能、丰富的数据结构和灵活的配置选项,已成为LLM部署中首选的缓存解决方案。
|
18天前
|
缓存 运维 监控
Redis 7.0 高性能缓存架构设计与优化
🌟蒋星熠Jaxonic,技术宇宙中的星际旅人。深耕Redis 7.0高性能缓存架构,探索函数化编程、多层缓存、集群优化与分片消息系统,用代码在二进制星河中谱写极客诗篇。
|
1月前
|
缓存 NoSQL 关系型数据库
Redis缓存和分布式锁
Redis 是一种高性能的键值存储系统,广泛用于缓存、消息队列和内存数据库。其典型应用包括缓解关系型数据库压力,通过缓存热点数据提高查询效率,支持高并发访问。此外,Redis 还可用于实现分布式锁,解决分布式系统中的资源竞争问题。文章还探讨了缓存的更新策略、缓存穿透与雪崩的解决方案,以及 Redlock 算法等关键技术。
|
3月前
|
缓存 数据库
如何解决缓存穿透?
对请求增加校验机制,如ID格式和位数校验,避免无效请求;缓存空值或特殊值防止缓存穿透;使用布隆过滤器拦截不存在的请求,减轻数据库压力。
46 0
|
4月前
|
监控 安全 Java
Java 开发中基于 Spring Boot 3.2 框架集成 MQTT 5.0 协议实现消息推送与订阅功能的技术方案解析
本文介绍基于Spring Boot 3.2集成MQTT 5.0的消息推送与订阅技术方案,涵盖核心技术栈选型(Spring Boot、Eclipse Paho、HiveMQ)、项目搭建与配置、消息发布与订阅服务实现,以及在智能家居控制系统中的应用实例。同时,详细探讨了安全增强(TLS/SSL)、性能优化(异步处理与背压控制)、测试监控及生产环境部署方案,为构建高可用、高性能的消息通信系统提供全面指导。附资源下载链接:[https://pan.quark.cn/s/14fcf913bae6](https://pan.quark.cn/s/14fcf913bae6)。
645 0

热门文章

最新文章