Spring Boot3 整合 Redis 后解决缓存穿透问题全解析
mhr18 2025-07-27 22:26 3 浏览 0 评论
在当今互联网软件开发领域,构建高效、稳定的应用系统是每个开发者的追求。对于从事互联网软件开发的人员来说,Spring Boot 和 Redis 都是极为常用的技术工具。当在 Spring Boot3 项目中整合 Redis 之后,如何有效解决缓存穿透问题,成为了提升系统性能的关键一环。
缓存穿透问题剖析
(一)缓存穿透的定义
缓存穿透指的是客户端请求的数据在缓存中不存在,并且在数据库中也不存在,导致请求直接穿透缓存层打到数据库上。正常情况下,应用程序会先查询缓存,如果缓存中有数据则直接返回,这样可以大大减轻数据库的压力。但当出现缓存穿透时,每次请求都无法命中缓存,只能去查询数据库,而数据库中又没有相应数据,这就使得数据库承受了大量不必要的查询压力,严重时可能导致数据库性能下降甚至崩溃。
(二)缓存穿透产生的原因
- 恶意攻击:恶意用户可能会构造大量不存在的数据请求,例如在电商系统中,攻击者可能会不断请求一些不存在的商品 ID,试图耗尽数据库资源,使系统瘫痪。
- 数据不一致:在某些情况下,可能由于业务逻辑错误或者数据同步延迟等原因,导致缓存中的数据与数据库中的数据不一致。例如,数据库中的某条数据被删除了,但缓存中对应的缓存数据没有及时更新或删除,此时如果有请求查询该数据,就会出现缓存穿透。
- 用户错误输入:用户在使用应用程序时,可能会因为误操作输入一些不合理的数据,而这些数据在数据库中并不存在,从而引发缓存穿透。
(三)缓存穿透带来的危害
缓存穿透对系统性能和稳定性的影响是巨大的。首先,它会使数据库的负载急剧增加,因为原本可以通过缓存处理的请求都直接落到了数据库上,可能导致数据库响应变慢,甚至出现卡顿现象。其次,如果数据库长期处于高负载状态,可能会引发其他问题,如数据库连接池耗尽、服务中断等,严重影响用户体验,甚至导致业务受损。
解决缓存穿透的方案
缓存空对象
原理:当查询的数据在数据库中不存在时,将一个特殊的空值(如null或者自定义的一个表示空值的对象)缓存起来,并设置一个较短的过期时间。这样,当后续再有相同的请求时,就可以直接从缓存中获取这个空值,而不会穿透到数据库。
示例代码(Java)
在 Spring Boot3 项目中,使用 RedisTemplate 来实现缓存空对象。假设我们有一个查询商品信息的方法getProduct:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
public Object getProduct(Long productId) {
Object product = redisTemplate.opsForValue().get("product:" + productId);
if (product == null) {
// 从数据库查询商品
product = queryProductFromDatabase(productId);
if (product == null) {
// 缓存空值
redisTemplate.opsForValue().set("product:" + productId, "null", 5, TimeUnit.MINUTES);
} else {
// 缓存商品信息
redisTemplate.opsForValue().set("product:" + productId, product);
}
}
return product;
}
private Object queryProductFromDatabase(Long productId) {
// 实际的数据库查询逻辑
// 这里简单返回null模拟数据库中不存在该商品
return null;
}
}
优缺点分析
优点:实现简单,对原有系统的侵入性较小,能够在一定程度上缓解缓存穿透问题。
缺点:会占用额外的缓存空间来存储这些空值,并且如果空值缓存的过期时间设置不合理,可能会导致在过期时间内,即使数据库中已经新增了对应的数据,查询仍然会返回空值,影响数据的实时性。
(二)布隆过滤器
原理:布隆过滤器是一种概率型数据结构,它通过多个哈希函数将一个元素映射到位数组中的多个位置,并将这些位置置为 1。当查询某个元素是否存在时,通过同样的哈希函数计算出对应的位置,如果这些位置上的值都是 1,则表示该元素可能存在;如果有任何一个位置的值为 0,则表示该元素一定不存在。在缓存系统中使用布隆过滤器,在查询数据之前先通过布隆过滤器判断数据是否可能存在,如果不可能存在,则直接返回,不再查询数据库,从而避免缓存穿透。
示例代码(结合 Spring Boot3 和 Redisson)
首先,在pom.xml中引入 Redisson 相关依赖:
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.16.1</version>
</dependency>
然后,在application.yml中配置 Redis 连接信息:
spring:
data:
redis:
host: localhost
port: 6379
password: 123456
timeout: 60000
database: 0
lettuce:
pool:
max-active: 8
max-idle: 8
min-idle: 0
max-wait: -1ms
接下来,编写使用布隆过滤器的代码:
import org.redisson.api.RBloomFilter;
import org.redisson.api.RedissonClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
@Service
public class BloomFilterService {
@Autowired
private RedissonClient redissonClient;
public void initBloomFilter() {
// 创建布隆过滤器,预计元素数量为1000000,误判率为0.01
RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("productBloomFilter");
bloomFilter.tryInit(1000000, 0.01);
// 假设从数据库中获取所有商品ID并添加到布隆过滤器中
// 这里简单模拟添加一些商品ID
String[] productIds = {"1", "2", "3", "4", "5"};
for (String productId : productIds) {
bloomFilter.add(productId);
}
}
public boolean mightContain(String productId) {
RBloomFilter<String> bloomFilter = redissonClient.getBloomFilter("productBloomFilter");
return bloomFilter.contains(productId);
}
}
在查询商品的方法中使用布隆过滤器
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class ProductService {
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Autowired
private BloomFilterService bloomFilterService;
public Object getProduct(String productId) {
if (!bloomFilterService.mightContain(productId)) {
return null;
}
Object product = redisTemplate.opsForValue().get("product:" + productId);
if (product == null) {
// 从数据库查询商品
product = queryProductFromDatabase(productId);
if (product == null) {
// 缓存空值
redisTemplate.opsForValue().set("product:" + productId, "null", 5, TimeUnit.MINUTES);
} else {
// 缓存商品信息
redisTemplate.opsForValue().set("product:" + productId, product);
}
}
return product;
}
private Object queryProductFromDatabase(String productId) {
// 实际的数据库查询逻辑
// 这里简单返回null模拟数据库中不存在该商品
return null;
}
}
优缺点分析
优点:能够显著减少无效的数据库查询,有效防止缓存穿透。布隆过滤器的空间效率和时间效率都很高,适合处理大规模数据的快速查询场景。
缺点:布隆过滤器存在一定的误判率,即可能会把不存在的数据误判为存在,这可能会导致一些正常的查询也被拦截。而且一旦布隆过滤器中添加的数据确定,很难进行删除操作,如果需要频繁添加和删除数据,使用布隆过滤器可能不太合适。
总结
在 Spring Boot3 整合 Redis 的开发场景中,缓存穿透问题是不容忽视的。通过缓存空对象和布隆过滤器这两种常见的解决方案,我们可以在一定程度上有效地解决缓存穿透问题,提升系统的性能和稳定性。缓存空对象方案简单直接,适合对数据实时性要求不是特别高,且缓存空间相对充足的场景;布隆过滤器则在处理大规模数据和高并发请求时表现出色,能够极大地减少数据库的压力,但需要注意其误判率的影响。
随着技术的不断发展,未来可能会出现更多更高效的解决缓存穿透问题的方案。作为互联网软件开发人员,我们需要持续关注行业动态,不断学习和掌握新的技术知识,以便在实际项目中能够根据具体的业务需求,选择最合适的技术方案,打造出更加健壮、高效的应用系统。
相关推荐
- 风险突出的高危端口汇总 一网打尽 !
-
高危端口一直是攻击者关注的焦点,了解这些端口的风险、攻击方式及防护策略至关重要。一、文件传输类端口1.TCP20/21:FTP服务端口FTP(文件传输协议)用于文件的上传和下载。其明文传输特性使得...
- 9. Redis Operator (2) —— Sentinel部署
-
0.简介上一篇,我们借由Redis的单机部署,学习了一下Operator的基本使用,今天,我们在此基础上,部署一下Redis的Sentinel模式。Sentinel本质上是为了解...
- Spring Boot3 整合 Redis 后解决缓存穿透问题全解析
-
在当今互联网软件开发领域,构建高效、稳定的应用系统是每个开发者的追求。对于从事互联网软件开发的人员来说,SpringBoot和Redis都是极为常用的技术工具。当在SpringBoot3...
- Spring Boot3 整合 Redis 后解决缓存雪崩问题全解析
-
在当今互联网软件开发领域,高并发、高性能的系统需求日益增长。对于从事互联网软件开发的人员来说,构建高效的缓存机制至关重要。SpringBoot3作为一款流行的Java框架,与Redis这一...
- Sa-Token 多账号体系下Redis持久化问题
-
在使用Sa-Token框架实现多账号体系时,当后端服务重启后,系统报错"未能获取对应StpLogic,type=XXX"。这种情况通常发生在配置了Redis持久化存储的场景下...
- 外贸独立站缓存迷惑行为:你的Redis可能正在制造更多问题!
-
上周帮一个深圳卖家排查网站卡顿,发现他们用Redis缓存了整站HTML——"你们这是把缓存当备份用呢?"结果每次更新产品都要手动清空缓存,编辑小哥差点辞职...最近对象缓存圈两大魔教...
- 别再用top和htop了,这几款终端神器让你的服务器状态一目了然
-
当top命令成为性能瓶颈:一个深夜运维的真实困境凌晨三点,服务器告警短信突然炸响。老王盯着屏幕上top命令的黑白界面,CPU使用率飙升到90%却找不到具体进程,内存占用数据分散在不同列,磁盘I/O更是...
- Redis学习笔记:管道(Pipelining)技术详解(第三章)
-
在掌握了Redis的基础命令后,如何进一步提升批量操作的效率?管道(Pipelining)技术是解决这一问题的关键。本章将深入解析管道的工作原理、使用场景及与其他技术的对比,帮助你在高并发场景下优化R...
- Redis8.0有哪些新特性(redis最新特性)
-
Redis8.0引入了多项新特性和功能增强,以下是其中的一些亮点:1、数据结构:向量集合(VectorSet):这是一种新的数据类型,专为向量相似性搜索设计。它基于有序集(sortedset)...
- Netty 的对象池(netty objectdecoder)
-
Netty是一个高性能的网络通信框架,广泛用于构建高并发、低延迟的TCP/UDP服务。为了提升性能,Netty内部大量使用了对象池(ObjectPool)技术来减少频繁创建和销毁对象带来的...
- Redis学习笔记:核心命令与数据类型操作指南(第二章)
-
上一章我们梳理了Redis的核心应用场景与选型逻辑,本章将聚焦Redis的命令体系,从键操作到各数据类型的核心命令,帮你快速掌握Redis的"操作语法"。一、键(Key)命令:Redi...
- Redis面试核心考点总结(覆盖 90% 的 Redis 面试场景)
-
一、基础核心数据类型与适用场景String:缓存、计数器(INCR)、分布式锁(SETNX)Hash:存储对象(用户信息、商品属性)List:消息队列(LPUSH/BRPOP)、时间线Set:标...
- Redis ListPack有哪些具体应用场景?
-
Redis的Listpack是一种紧凑的数据结构,适用于存储少量数据。它被设计为ziplist的一种改进版本,旨在解决ziplist中存在的连锁更新问题,并提供更高效的内存使用和访问速度。以下是Lis...
- SpringBoot实现单点登录(SSO)的4种方案
-
单点登录(SingleSign-On,SSO)是企业应用系统中常见的用户认证方案,它允许用户使用一组凭证访问多个相关但独立的系统,无需重复登录。对于拥有多个应用的企业来说,SSO可以显著提升用户体验...
- 刚刚,给学妹普及了登录的两大绝学
-
今天跟大家聊一个比较基础的话题,就是实现登录的方式有哪些?适合刚入行的朋友。华山之Session绝学Session我们称之为会话控制,是一种在服务器端保持会话状态的解决方案。通俗点来讲就是客户...
你 发表评论:
欢迎- 一周热门
- 最近发表
-
- 风险突出的高危端口汇总 一网打尽 !
- 9. Redis Operator (2) —— Sentinel部署
- Spring Boot3 整合 Redis 后解决缓存穿透问题全解析
- Spring Boot3 整合 Redis 后解决缓存雪崩问题全解析
- Sa-Token 多账号体系下Redis持久化问题
- 外贸独立站缓存迷惑行为:你的Redis可能正在制造更多问题!
- 别再用top和htop了,这几款终端神器让你的服务器状态一目了然
- Redis学习笔记:管道(Pipelining)技术详解(第三章)
- Redis8.0有哪些新特性(redis最新特性)
- Netty 的对象池(netty objectdecoder)
- 标签列表
-
- oracle位图索引 (74)
- oracle批量插入数据 (65)
- oracle事务隔离级别 (59)
- oracle主从同步 (56)
- oracle 乐观锁 (53)
- redis 命令 (83)
- php redis (97)
- redis 存储 (67)
- redis 锁 (74)
- 启动 redis (73)
- redis 时间 (60)
- redis 删除 (69)
- redis内存 (64)
- redis并发 (53)
- redis 主从 (71)
- redis同步 (53)
- redis结构 (53)
- redis 订阅 (54)
- redis 登录 (62)
- redis 面试 (58)
- redis问题 (54)
- 阿里 redis (59)
- redis的缓存 (55)
- lua redis (58)
- redis 连接池 (61)