Spring Boot 项目中 Redis 常见问题及解决方案

目录

  1. 缓存穿透
  2. 缓存雪崩
  3. 缓存击穿
  4. Redis 连接池耗尽
  5. Redis 序列化问题
  6. 总结

1. 缓存穿透

问题描述

缓存穿透是指查询一个不存在的数据,由于缓存中没有该数据,请求会直接打到数据库上,导致数据库压力过大。

解决方案

  1. 缓存空值:即使查询的数据不存在,也将空值缓存起来,并设置一个较短的过期时间。
  2. 布隆过滤器:在查询缓存之前,先通过布隆过滤器判断数据是否存在。

示例代码

@Service
public class UserService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public User getUserById(Long id) {String key = "user:" + id;// 从缓存中获取数据User user = (User) redisTemplate.opsForValue().get(key);if (user != null) {return user;}// 缓存中不存在,查询数据库user = userRepository.findById(id).orElse(null);if (user == null) {// 缓存空值,设置较短的过期时间redisTemplate.opsForValue().set(key, null, 60, TimeUnit.SECONDS);} else {// 缓存查询结果redisTemplate.opsForValue().set(key, user, 1, TimeUnit.HOURS);}return user;}
}

2. 缓存雪崩

问题描述

缓存雪崩是指大量缓存数据在同一时间失效,导致所有请求都打到数据库上,造成数据库压力过大甚至崩溃。

解决方案

  1. 设置不同的过期时间:为缓存数据设置随机的过期时间,避免大量缓存同时失效。
  2. 使用分布式锁:在缓存失效时,使用分布式锁保证只有一个线程去加载数据。

示例代码

@Service
public class ProductService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;@Autowiredprivate RedissonClient redissonClient;public Product getProductById(Long id) {String key = "product:" + id;Product product = (Product) redisTemplate.opsForValue().get(key);if (product != null) {return product;}// 使用分布式锁防止缓存击穿RLock lock = redissonClient.getLock("lock:" + key);try {lock.lock();// 双重检查,防止其他线程已经加载了数据product = (Product) redisTemplate.opsForValue().get(key);if (product != null) {return product;}// 查询数据库product = productRepository.findById(id).orElse(null);if (product != null) {// 设置随机的过期时间int expireTime = 3600 + new Random().nextInt(600); // 1小时 + 随机10分钟redisTemplate.opsForValue().set(key, product, expireTime, TimeUnit.SECONDS);}} finally {lock.unlock();}return product;}
}

3. 缓存击穿

问题描述

缓存击穿是指某个热点数据在缓存中失效后,大量请求同时打到数据库上,导致数据库压力过大。

解决方案

  1. 使用互斥锁:在缓存失效时,使用互斥锁保证只有一个线程去加载数据。
  2. 永不过期策略:对热点数据设置永不过期,通过后台任务定期更新缓存。

示例代码

@Service
public class HotDataService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;public String getHotData() {String key = "hot_data";String data = (String) redisTemplate.opsForValue().get(key);if (data != null) {return data;}// 使用 Redis 的 SETNX 实现互斥锁String lockKey = "lock:" + key;boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "locked", 10, TimeUnit.SECONDS);if (locked) {try {// 双重检查data = (String) redisTemplate.opsForValue().get(key);if (data != null) {return data;}// 模拟从数据库加载热点数据data = loadHotDataFromDB();redisTemplate.opsForValue().set(key, data, 1, TimeUnit.HOURS);} finally {// 释放锁redisTemplate.delete(lockKey);}} else {// 未获取到锁,等待重试try {Thread.sleep(100);return getHotData(); // 重试} catch (InterruptedException e) {Thread.currentThread().interrupt();}}return data;}private String loadHotDataFromDB() {// 模拟数据库查询return "hot_data_from_db";}
}

4. Redis 连接池耗尽

问题描述

在高并发场景下,Redis 连接池可能会被耗尽,导致请求失败。

解决方案

  1. 增加连接池大小:根据实际需求调整连接池的最大连接数。
  2. 优化连接使用:确保每次操作 Redis 后及时释放连接。

示例代码

application.yml 中配置连接池:

spring:redis:host: localhostport: 6379lettuce:pool:max-active: 50  # 最大连接数max-idle: 10   # 最大空闲连接数min-idle: 5    # 最小空闲连接数

5. Redis 序列化问题

问题描述

默认情况下,Spring Boot 使用 JdkSerializationRedisSerializer 进行序列化,可能导致存储的数据不易阅读或兼容性问题。

解决方案

使用更高效的序列化方式,如 Jackson2JsonRedisSerializerStringRedisSerializer

示例代码

@Configuration
public class RedisConfig {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(factory);// 使用 Jackson2JsonRedisSerializer 序列化值Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);template.setValueSerializer(serializer);template.setHashValueSerializer(serializer);// 使用 StringRedisSerializer 序列化键template.setKeySerializer(new StringRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());return template;}
}

6. 总结

在 Spring Boot 项目中使用 Redis 时,可能会遇到缓存穿透、缓存雪崩、缓存击穿、连接池耗尽以及序列化等问题。通过合理的缓存策略、分布式锁、连接池配置和序列化方式,可以有效解决这些问题,提升系统的稳定性和性能。希望本文的解决方案和示例代码能帮助到你!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/29831.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

KubeKey 与 KubeSphere:快速构建 Kubernetes 集群

深度解析 KubeKey 与 KubeSphere&#xff1a;快速构建现代化 Kubernetes 集群 一、技术栈概述 KubeKey&#xff08;KK&#xff09;是由 KubeSphere 团队开发的轻量级 Kubernetes 集群部署工具&#xff0c;其优势在于&#xff1a; 基于 Ansible 的安装程序具有大量软件依赖性…

C++编写Redis客户端

目录 安装redis-plus-plus库 ​编辑 编译Credis客户端 redis的通用命令使用 get/set exists del keys expire /ttl type string类型核心操作 set和get set带有超时时间 set带有NX string带有XX mset mget getrange和setrange incr和decr list类型核心操作…

从零开始实现大语言模型(十三):预训练大语言模型GPTModel

1. 前言 使用梯度下降算法通过下一个token预测任务预训练大语言模型GPTModel&#xff0c;前向传播流程每次会输入一个batch的长度均为context_len的训练样本&#xff0c;执行 batch_size context_len \text{batch\_size}\times\text{context\_len} batch_sizecontext_len次下…

JavaScript(最后一个元素的索引就是数组的长度减 1)array.length - 1

在不同的编程语言中&#xff0c;表示数组中最后一个元素的方法略有不同&#xff0c;但基本思路都是利用数组的长度或索引来实现。 以下是一些常见编程语言中获取数组最后一个元素的方法&#xff1a; 1. JavaScript: 使用 array.length - 1 索引: 这是最常见和传统的方法。Java…

RV1126+FFMPEG多路码流监控项目

一.项目介绍&#xff1a; 本项目采用的是易百纳RV1126开发板和CMOS摄像头&#xff0c;使用的推流框架是FFMPEG开源项目。这个项目的工作流程如下(如上图)&#xff1a;通过采集摄像头的VI模块&#xff0c;再通过硬件编码VENC模块进行H264/H265的编码压缩&#xff0c;并把压缩后的…

Python组合数据类型(一)

目录 一、数据类型 1、基本数据类型 2、组合数据类型 二、介绍两个函数 1、 isinstance函数 2、len函数 三、Python指针 1、指针 2、is运算符和的区别 3、列表的指针 四、函数参数的传递 1、例子一 2、例子二 五、字符串详解 1、转义字符 2、字符串的切片 3、字…

Doris vs ClickHouse 企业级实时分析引擎怎么选?

Apache Doris 与 ClickHouse 同作为OLAP领域的佼佼者&#xff0c;在企业级实时分析引擎该如何选择呢。本文将详细介绍 Doris 的优势&#xff0c;并通过直观对比展示两者的关键差异&#xff0c;同时分享一个企业成功用 Doris 替换 ClickHouse 的实践案例&#xff0c;帮助您做出明…

【ThreeJS Basics 09】Debug

文章目录 简介从 dat.GUI 到 lil-gui例子安装 lil-gui 并实例化不同类型的调整改变位置针对非属性的调整复选框颜色 功能/按钮调整几何形状文件夹调整 GUI宽度标题关闭文件夹隐藏按键切换 结论 简介 每一个创意项目的一个基本方面是能够轻松调整。开发人员和参与项目的其他参与…

Android Native 之 文件系统挂载

一、文件系统挂载流程概述 二、文件系统挂载流程细节 1、Init启动阶段 众所周知&#xff0c;init进程为android系统的第一个进程&#xff0c;也是native世界的开端&#xff0c;要想让整个android世界能够稳定的运行&#xff0c;文件系统的创建和初始化是必不可少的&#xff…

Chain of Draft: 借鉴人类草稿思维让大型语言模型更快地思考

这个研究探讨了大型语言模型&#xff08;LLMs&#xff09;在执行复杂推理任务时面临的计算资源消耗与响应延迟问题。研究特别聚焦于思维链&#xff08;Chain-of-Thought, CoT&#xff09;提示范式的效率局限性。CoT虽然有效&#xff0c;但在推理过程中需要生成冗长、详尽的逐步…

《A++ 敏捷开发》- 18 软件需求

需求并不是关于需求 (Requirements are not really about requirements) 大家去公共图书馆寄存物品&#xff0c;以前都是扫二维码开箱&#xff0c;有些图书馆升级了使用指纹识别。 “是否新方法比以前好&#xff1f;”我问年轻的开发人员。 “当然用指纹识别好。新技术&#x…

【智能体架构:Agent】LangChain智能体类型ReAct、Self-ASK的区别

1. 什么是智能体 将大语言模型作为一个推理引擎。给定一个任务&#xff0c; 智能体自动生成完成任务所需步骤&#xff0c; 执行相应动作&#xff08;例如选择并调用工具&#xff09;&#xff0c; 直到任务完成。 2. 先定义工具&#xff1a;Tools 可以是一个函数或三方 API也…

Vue进阶之Vue3源码解析(一)

Vue3源码解析 目录结构编译compiler-corepackage.jsonsrc/index.ts 入口文件src/compile.ts生成ASTsrc/parse.ts 代码转换src/transform.ts几种策略模式src/transforms/transformElement.tssrc/transforms/transformText.tssrc/transforms/transformExpression.ts 代码生成src/…

servlet tomcat

在spring-mvc demo程序运行到DispatcherServlet的mvc处理 一文中&#xff0c;我们实践了浏览器输入一个请求&#xff0c;然后到SpringMvc的DispatcherServlet处理的整个流程. 设计上这些都是tomcat servlet的处理 那么究竟这是怎么到DispatcherServlet处理的&#xff0c;本文将…

【我的待办(MyTodolists)-免费无内购的 IOS 应用】

我的待办&#xff08;MyTodolists&#xff09; 我的待办&#xff1a;智能任务管理助手应用说明主要功能为什么选择"我的待办"&#xff1f;隐私保障使用截图 我的待办&#xff1a;智能任务管理助手 应用说明 "我的待办"是一款智能化的任务管理应用&#x…

GCC RISCV 后端 -- C语言语法分析过程

在 GCC 编译一个 C 源代码时&#xff0c;先会通过宏处理&#xff0c;形成 一个叫转译单元&#xff08;translation_unit&#xff09;&#xff0c;接着进行语法分析&#xff0c;C 的语法分析入口是 static void c_parser_translation_unit(c_parser *parser); 接着就通过类似递…

Vim复制内容到系统剪切板

参考链接 【Vim】Vim 中将文件内容复制到系统剪切板的方法_vi 复制到系统剪贴板-CSDN博客 [转]vim如何复制到系统剪贴板 - biiigwang - 博客园 1. 确定Vim是否支持复制到系统剪切板 输入命令 vim --version | grep clipboard 如果是开头&#xff0c;说明支持系统剪切板&…

测试用大模型组词

已经把hanzi-writer的js的调用、hanzi-writer调用的数千个汉字的json文件&#xff0c;全都放在本地了。虽然用的办法还是比较笨的。我注意到 大模型也可以部署本地&#xff0c;虽然使用频率低的情况下不划算。 尝试直接通过html的javascript通过api key调用大语言模型&#x…

华为eNSP:配置单区域OSPF

一、什么是OSPF&#xff1f; OSPF&#xff08;Open Shortest Path First&#xff0c;开放最短路径优先&#xff09;是一种链路状态路由协议&#xff0c;属于内部网关协议&#xff08;IGP&#xff09;&#xff0c;主要用于在单一自治系统&#xff08;AS&#xff09;内部动态发现…

P62 线程

这篇文章我们来讲一下线程。截止到目前&#xff0c;我们的代码都是在单线程上运行的&#xff0c;现在看起来没有什么问题&#xff0c;但是目前所有的计算机几乎都不只有一个逻辑线程&#xff0c;所以如果我们一直使用单线程运行&#xff0c;这样的话效率会很低。尤其是如果我们…