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


相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
24天前
|
缓存 NoSQL 关系型数据库
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
美团面试:MySQL有1000w数据,redis只存20w的数据,如何做 缓存 设计?
|
1月前
|
缓存 NoSQL Java
Redis+Caffeine构建高性能二级缓存
大家好,我是摘星。今天为大家带来的是Redis+Caffeine构建高性能二级缓存,废话不多说直接开始~
242 0
|
1月前
|
消息中间件 缓存 NoSQL
基于Spring Data Redis与RabbitMQ实现字符串缓存和计数功能(数据同步)
总的来说,借助Spring Data Redis和RabbitMQ,我们可以轻松实现字符串缓存和计数的功能。而关键的部分不过是一些"厨房的套路",一旦你掌握了这些套路,那么你就像厨师一样可以准备出一道道饕餮美食了。通过这种方式促进数据处理效率无疑将大大提高我们的生产力。
104 32
|
1月前
|
缓存 NoSQL Java
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
60 5
Redis:现代服务端开发的缓存基石与电商实践-优雅草卓伊凡
|
17天前
|
机器学习/深度学习 数据采集 人机交互
springboot+redis互联网医院智能导诊系统源码,基于医疗大模型、知识图谱、人机交互方式实现
智能导诊系统基于医疗大模型、知识图谱与人机交互技术,解决患者“知症不知病”“挂错号”等问题。通过多模态交互(语音、文字、图片等)收集病情信息,结合医学知识图谱和深度推理,实现精准的科室推荐和分级诊疗引导。系统支持基于规则模板和数据模型两种开发原理:前者依赖人工设定症状-科室规则,后者通过机器学习或深度学习分析问诊数据。其特点包括快速病情收集、智能病症关联推理、最佳就医推荐、分级导流以及与院内平台联动,提升患者就诊效率和服务体验。技术架构采用 SpringBoot+Redis+MyBatis Plus+MySQL+RocketMQ,确保高效稳定运行。
67 0
|
2月前
|
人工智能 缓存 NoSQL
Redis 与 AI:从缓存到智能搜索的融合之路
Redis 已从传统缓存系统发展为强大的 AI 支持平台,其向量数据库功能和 RedisAI 模块为核心,支持高维向量存储、相似性搜索及模型服务。文章探讨了 Redis 在实时数据缓存、语义搜索与会话持久化中的应用场景,并通过代码案例展示了与 Spring Boot 的集成方式。总结来看,Redis 结合 AI 技术,为现代应用提供高效、灵活的解决方案。
|
3月前
|
NoSQL Java API
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Spring Boot 集成 Redis
本文介绍了在Spring Boot中集成Redis的方法,包括依赖导入、Redis配置及常用API的使用。通过导入`spring-boot-starter-data-redis`依赖和配置`application.yml`文件,可轻松实现Redis集成。文中详细讲解了StringRedisTemplate的使用,适用于字符串操作,并结合FastJSON将实体类转换为JSON存储。还展示了Redis的string、hash和list类型的操作示例。最后总结了Redis在缓存和高并发场景中的应用价值,并提供课程源代码下载链接。
218 0
|
3月前
|
NoSQL Java Redis
微服务——SpringBoot使用归纳——Spring Boot 中集成Redis——Redis 安装
本教程介绍在 VMware 虚拟机(CentOS 7)或阿里云服务器中安装 Redis 的过程,包括安装 gcc 编译环境、下载 Redis(官网或 wget)、解压安装、修改配置文件(如 bind、daemonize、requirepass 等设置)、启动 Redis 服务及测试客户端连接。通过 set 和 get 命令验证安装是否成功。适用于初学者快速上手 Redis 部署。
63 0
|
缓存 NoSQL 数据库
什么是redis的缓存雪崩与缓存穿透
什么是redis的缓存雪崩与缓存穿透今天来分享一下Redis几道常见的面试题: 如何解决缓存雪崩?如何解决缓存穿透?如何保证缓存与数据库双写时一致的问题?一、缓存雪崩1.1 什么是缓存雪崩?首先我们先来回答一下我们为什么要用缓存(Redis): 1、提高性能能:缓存查询是纯内存访问,而硬盘是磁盘访问,因此缓存查询速度比数据库查询速度快 2、提高并发能力:缓存分组了部分请求,支持更高的并发 现在有个问题,如果我们的缓存挂掉了,这意味着我们的全部请求都跑去数据库了。
1315 0
|
3月前
|
缓存 监控 NoSQL
Redis--缓存击穿、缓存穿透、缓存雪崩
缓存击穿、缓存穿透和缓存雪崩是Redis使用过程中可能遇到的常见问题。理解这些问题的成因并采取相应的解决措施,可以有效提升系统的稳定性和性能。在实际应用中,应根据具体场景,选择合适的解决方案,并持续监控和优化缓存策略,以应对不断变化的业务需求。
173 29