SpringBoot整合缓存2-Redis

news/2025/10/24 19:00:53/文章来源:https://www.cnblogs.com/smalldong/p/19164057

一、是什么:缓存的基本概念

缓存是一种存储技术,用于临时保存频繁访问的数据,以减少对数据库的直接访问,从而提升系统响应速度和降低数据库压力。在本案例中,我们使用 Redis 作为缓存中间件,结合 SpringBoot 和 MyBatis-Plus 实现对图书数据的缓存管理。

二、为什么:使用缓存的原因

  1. 提升性能:Redis 是内存数据库,读写速度远快于磁盘数据库(如 MySQL),减少数据库 IO 操作。
  2. 减轻数据库压力:高频访问的数据从缓存获取,降低数据库的查询负载。
  3. 改善用户体验:减少接口响应时间,提升系统流畅度。

三、怎么做:具体实现步骤

image-20251024185643182

1. 环境准备

  • JDK
  • SpringBoot 2.7.x
  • MySQL
  • Redis
  • MyBatis-Plus

数据库脚本

-- 创建数据库
CREATE DATABASE IF NOT EXISTS book_cache_demo DEFAULT CHARACTER SET utf8;-- 使用数据库
USE book_cache_demo;-- 创建图书表
CREATE TABLE `t_book` (`id` bigint(20) NOT NULL AUTO_INCREMENT COMMENT '图书ID',`book_name` varchar(100) NOT NULL COMMENT '图书名称',`author` varchar(50) NOT NULL COMMENT '作者',`publish_time` date DEFAULT NULL COMMENT '出版时间',`price` decimal(10,2) NOT NULL COMMENT '价格',PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COMMENT='图书表';

2. 创建项目并添加依赖

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-parent</artifactId><version>2.7.4</version><relativePath/> <!-- lookup parent from repository --></parent><groupId>com.yqd</groupId><artifactId>RedisCache-Demo</artifactId><version>1.0-SNAPSHOT</version><packaging>jar</packaging><name>RedisCache-Demo</name><url>http://maven.apache.org</url><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><!-- Spring Web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- 缓存抽象层 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!-- Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- MyBatis-Plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3.1</version></dependency><!-- MySQL驱动 --><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId><scope>runtime</scope></dependency><!-- Lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency></dependencies><build><plugins><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

3. 配置文件(application.yml)

spring:# 数据库配置datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/book_cache_demo?useSSL=false&serverTimezone=UTCusername: root  # 替换为你的MySQL用户名password: 123456  # 替换为你的MySQL密码# Redis配置redis:host: localhost  # Redis地址port: 6379       # Redis端口password:        # Redis密码(无密码则留空)timeout: 2000ms  # 连接超时时间# 缓存配置(Redis)cache:type: redisredis:time-to-live: 60000ms  # 缓存过期时间(60秒)key-prefix: "user:"    # 缓存key前缀(避免与其他缓存冲突)cache-null-values: false  # 不缓存null值# MyBatis-Plus配置
mybatis-plus:mapper-locations: classpath:mapper/*.xmltype-aliases-package: com.example.entityconfiguration:log-impl: org.apache.ibatis.logging.stdout.StdOutImpl  # 打印SQL,验证缓存是否生效

4. 核心代码实现

(1)实体类(Entity)

package com.yqd.entity;import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.io.Serializable;  // 导入Serializable
import java.math.BigDecimal;
import java.util.Date;@Data
@TableName("t_book")
public class Book implements Serializable {  // 实现Serializable接口private static final long serialVersionUID = 1L;  // 序列化版本号(建议添加)@TableId(type = IdType.AUTO)private Long id;private String bookName;private String author;private Date publishTime;private BigDecimal price;
}

(2)Mapper 接口(继承 MyBatis-Plus 的 BaseMapper)

package com.yqd.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;import com.yqd.entity.Book;
import org.apache.ibatis.annotations.Mapper;@Mapper
public interface BookMapper extends BaseMapper<Book> {// 继承BaseMapper,无需手动编写CRUD方法
}

(3)Service 层(实现缓存逻辑)

package com.yqd.service;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yqd.entity.Book;
import com.yqd.mapper.BookMapper;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;@Service
@CacheConfig(cacheNames = "book")  // 缓存名称,与前缀拼接为"book:xxx"
public class BookService extends ServiceImpl<BookMapper, Book> {/*** 查询图书:优先从Redis缓存获取,无则查库并写入缓存*/@Cacheable(key = "#id")  // 缓存key:book:idpublic Book getById(Long id) {System.out.println("【数据库查询】图书ID=" + id);  // 验证是否走缓存return baseMapper.selectById(id);}/*** 新增图书:入库后同步写入缓存*/@CachePut(key = "#result.id")  // 用新增图书的id作为缓存keypublic Book add(Book book) {baseMapper.insert(book);return book;}/*** 更新图书:更新数据库后,同步更新缓存*/@CachePut(key = "#book.id")  // 缓存key:book:book.idpublic Book update(Book book) {baseMapper.updateById(book);return book;}/*** 删除图书:删除数据库后,清除对应缓存*/@CacheEvict(key = "#id")  // 清除key为book:id的缓存public void delete(Long id) {baseMapper.deleteById(id);}/*** 清除所有图书缓存*/@CacheEvict(allEntries = true)  // 清除所有key以book:开头的缓存public void clearAllCache() {System.out.println("【清除所有图书缓存】");}
}

(4)Controller 层(接口测试)

package com.yqd.controller;import com.yqd.entity.Book;
import com.yqd.service.BookService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;@RestController
@RequestMapping("/books")
public class BookController {@Autowiredprivate BookService bookService;// 查询图书@GetMapping("/{id}")public Book get(@PathVariable Long id) {return bookService.getById(id);}// 新增图书@PostMappingpublic Book add(@RequestBody Book book) {return bookService.add(book);}// 更新图书@PutMappingpublic Book update(@RequestBody Book book) {return bookService.update(book);}// 删除图书@DeleteMapping("/{id}")public String delete(@PathVariable Long id) {bookService.delete(id);return "删除成功";}// 清除所有缓存@DeleteMapping("/clearCache")public String clearCache() {bookService.clearAllCache();return "所有图书缓存已清除";}
}

(5)启动类

package com.yqd;import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;@SpringBootApplication
@EnableCaching  // 开启缓存功能
@MapperScan("com.yqd.mapper")  // 扫描Mapper接口
public class RedisCacheDemo {public static void main(String[] args) {SpringApplication.run(RedisCacheDemo.class, args);}
}

(6)Redis配置类(可选)

package com.yqd.config;import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.time.Duration;@EnableCaching
@Configuration
public class RedisCacheConfig extends CachingConfigurerSupport {@Beanpublic RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<String, Object> redisTemplate = new RedisTemplate<>();RedisSerializer<String> redisSerializer = new StringRedisSerializer();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.setConnectionFactory(redisConnectionFactory);// key序列化redisTemplate.setKeySerializer(redisSerializer);// value序列化redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);// hashvalue序列化redisTemplate.setHashValueSerializer(jackson2JsonRedisSerializer);return redisTemplate;}@Beanpublic CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {RedisSerializer<String> redisSerializer = new StringRedisSerializer();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);RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()// 过期时间500秒.entryTtl(Duration.ofSeconds(500)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(redisSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jackson2JsonRedisSerializer)).disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(redisCacheConfiguration).build();return cacheManager;}
}

四、测试验证

  1. 初始化数据:向t_book表插入测试数据(如INSERT INTO t_book(book_name, author, publish_time, price) VALUES('SpringBoot实战', '张三', '2023-01-01', 59.90);)。
  2. 查询测试:
    • 首次访问GET http://localhost:8080/books/1,控制台打印 “从数据库获取数据并缓存”。
    • 再次访问同一接口,控制台打印 “从缓存获取数据”,说明缓存生效。
  3. 更新测试:
    • 调用PUT http://localhost:8080/books更新数据,控制台显示 “删除缓存”。
    • 再次查询,会重新从数据库获取并更新缓存。
  4. 删除测试:
    • 调用DELETE http://localhost:8080/books/1,缓存被删除,再次查询会返回空(数据库记录已删除)。

五、缓存注意事项

  1. 缓存一致性:更新 / 删除数据时需同步操作缓存(删除或更新),避免缓存与数据库数据不一致。
  2. 过期时间:设置缓存过期时间(如 30 分钟),防止缓存无限期存储导致内存溢出或数据过时。
  3. 缓存穿透:对不存在的 ID 查询,可缓存空值(短期),避免频繁访问数据库。
  4. 缓存雪崩:不同 key 设置随机过期时间,避免同时失效导致数据库压力骤增。

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

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

相关文章

数字人企业:推荐数字人TOP3公司

数字人企业:技术浪潮中的新势力崛起 解码数字人企业核心赛道,看头部玩家如何重塑产业格局 从概念到落地,数字人企业如何撬动千亿级市场? 数字人企业排行榜:技术、资本与生态的三重博弈 在数字人产业蓬勃发展的当下…

数字人平台:重点推荐优质数字人公司

数字人企业:未来产业的新势力崛起 数字人企业技术赛道解析与头部玩家前瞻 从技术到场景,数字人企业如何重塑产业生态? 一、数字人企业排行榜:技术实力与商业价值的双重验证 在人工智能与元宇宙浪潮的推动下,数字人…

深入解析:【Java系列课程Java学前须知】第3课 JDK,JVM,JRE的区别和优缺

深入解析:【Java系列课程Java学前须知】第3课 JDK,JVM,JRE的区别和优缺pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family…

10.24 CSP-S 模拟37 改题记录

唐人唐题唐错场HZOJ 卸载前面 好一个模拟赛。主打一个唐人做唐题犯唐错。然后大概就是前三题没啥难度,没写过是因为我太唐了。然后因为T1复杂度不对,虽然过了甚至还是最优解,但我要将其归为没A。所以我将继续保持连…

395.至少有K个重复字符的最长字串

395.至少有K个重复字符的最长字串给你一个字符串 s 和一个整数 k ,请你找出 s 中的最长子串, 要求该子串中的每一字符出现次数都不少于 k 。返回这一子串的长度。 如果不存在这样的子字符串,则返回 0。 这题首先想到…

NOI25D2T2

NOI25D2T2 序列变换 DP独立切黑祭(虽然花了很长时间)。思路比较自然,只是思维链比较长。 step 1 消出一个 \(0\) 后,只能向两侧继续消除。想到把操作刻画为从一个区间的中间向两边消除,即将 ###...### 变为 #<…

详细介绍:云手机远程控制的作用

详细介绍:云手机远程控制的作用pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco&qu…

数字人企业:数字人公司重点推荐与选择指南

数字人企业:AI浪潮下的产业新势力崛起 解码数字人企业技术壁垒与市场格局 从实验室到产业:数字人企业的创新突围路径 一、数字人企业排行榜:技术驱动下的行业领跑者 在元宇宙与AI技术的双重推动下,数字人企业正从概…

10.24模拟赛

チーム分け 题面 题意 每个点有限制形如这个点分的组人数 \(\le a_i\),问合法方案数。\(n\le 1000\) 题解 一个组内的限制只与 \(a_i\) 最小的元素相关,不妨将 \(a_i\) 从大到小排序延后计算贡献。 设 \(dp_{i,j}\) …

据说每邀请一位朋友加入Comet,您可以获得10刀乐奖励:D

Comet AI浏览器真的撒钱抢用户了! 最近OpenAI发布了AI浏览器Atlas,正式加入浏览器大战。 Perplexity马上就坐不住了,开启了撒钱抢人计划,现在只要分享Comet浏览器的邀请链接,好友通过链接下载并注册账户使用浏览器…

2025.10.24NOIP

T2.LCA \(n\)个点\(m\)条边无向图,第\(i\)条边的边权为\(i\),按以下方式建树,问以哪些节点做根节点能建出最小生成树,

writing sentences

1. The necessity of ... is defining feature of contemparay globalized society. 2. I am firmly of the conviction that the long-term advantages for .... outweight the .... 3. The most palpable is the psy…

小程序 访问第三方网页

// wxml文件 <web-view src="https://www.baidu.com" bindload="bindload" binderror="binderror"></web-view>// js文件 // 网页加载成功时触发此事件 bindload(res) { co…

王炸!OpenAI 发布 Atlas 浏览器!!

大家好,我是R哥。 说到 AI 浏览器,有大名鼎鼎的 Dia、Comet,谷歌的 Chrome + Gemini 也在蠢蠢欲动,似乎 AI 浏览器的战场还没有彻底打响,最近 OpenAI 也下场了。。 最近 OpenAI 亲自下场,推出了自家的 AI 浏览器…

国产开源数据库调研项目的LaTeX专业排版实践

国产开源数据库调研项目的LaTeX专业排版实践国产开源数据库调研项目的LaTeX专业排版实践 在上一篇文章《LaTeX 项目结构优化:从基础到专业》中,我们探讨了模块化LaTeX项目的基本结构。本文将通过一个实际项目——&qu…

Asterix cat-062 ,航班号字段的编码解码

Asterix cat-062 ,航班号字段的编码解码Data Item I062/245, Target Identification 解码:CQH87920d1238df9ca0 = 000011010001001000111000110111111001110010100000(这里二进制数要补零至48位)再按6比特分解成8段…

AI优化企业:GEO公司技术先驱

AI优化企业:解码智能时代的流量密码与商业增长引擎 2025年AI优化企业排行榜与全域流量实战指南 AI优化如何重塑企业数字竞争力? 在生成式AI重构搜索生态的2025年,企业竞争的核心已从“流量获取”转向“算法穿透”。…

题3

10.24 P5658 [CSP-S2019] 括号树 这个实际上就是给定一个括号序列\(a\),然后对于每一个\(i\),来说,求出\([1,i]\)的所有合法括号子串(发现这个其实只需要求以\(i\)为结尾的合法括号后缀然后做前缀和就行了)那么只…

CompletableFuture串联多个异步任务实践

CompletableFuture串联多个异步任务实践java 多线程中对于一个任务A完成了,任务B才开始;任务B完成了,任务C才开始;’任务C完成,任务D才开始;每个任务都是一个异步任务列。 废话少说,直接看代码,调试理解static…

课后作业4

https://files.cnblogs.com/files/blogs/847991/动手动脑4.zip?t=1761301142&download=true