SpringBoot整合缓存2-Redis
一、是什么:缓存的基本概念
缓存是一种存储技术,用于临时保存频繁访问的数据,以减少对数据库的直接访问,从而提升系统响应速度和降低数据库压力。在本案例中,我们使用 Redis 作为缓存中间件,结合 SpringBoot 和 MyBatis-Plus 实现对图书数据的缓存管理。
二、为什么:使用缓存的原因
- 提升性能:Redis 是内存数据库,读写速度远快于磁盘数据库(如 MySQL),减少数据库 IO 操作。
- 减轻数据库压力:高频访问的数据从缓存获取,降低数据库的查询负载。
- 改善用户体验:减少接口响应时间,提升系统流畅度。
三、怎么做:具体实现步骤

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.Driver
url: jdbc:mysql://localhost:3306/book_cache_demo?useSSL=false&serverTimezone=UTC
username: root # 替换为你的MySQL用户名
password: 123456 # 替换为你的MySQL密码
# Redis配置
redis:
host: localhost # Redis地址
port: 6379 # Redis端口
password: # Redis密码(无密码则留空)
timeout: 2000ms # 连接超时时间
# 缓存配置(Redis)
cache:
type: redis
redis:
time-to-live: 60000ms # 缓存过期时间(60秒)
key-prefix: "user:" # 缓存key前缀(避免与其他缓存冲突)
cache-null-values: false # 不缓存null值
# MyBatis-Plus配置
mybatis-plus:
mapper-locations: classpath:mapper/*.xml
type-aliases-package: com.example.entity
configuration:
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:id
public Book getById(Long id) {
System.out.println("【数据库查询】图书ID=" + id); // 验证是否走缓存
return baseMapper.selectById(id);
}
/**
* 新增图书:入库后同步写入缓存
*/
@CachePut(key = "#result.id") // 用新增图书的id作为缓存key
public Book add(Book book) {
baseMapper.insert(book);
return book;
}
/**
* 更新图书:更新数据库后,同步更新缓存
*/
@CachePut(key = "#book.id") // 缓存key:book:book.id
public 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 {
@Autowired
private BookService bookService;
// 查询图书
@GetMapping("/{id}")
public Book get(@PathVariable Long id) {
return bookService.getById(id);
}
// 新增图书
@PostMapping
public Book add(@RequestBody Book book) {
return bookService.add(book);
}
// 更新图书
@PutMapping
public 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 {
@Bean
public 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;
}
@Bean
public 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;
}
}
四、测试验证
- 初始化数据:向
t_book表插入测试数据(如INSERT INTO t_book(book_name, author, publish_time, price) VALUES('SpringBoot实战', '张三', '2023-01-01', 59.90);)。 - 查询测试:
- 首次访问
GET http://localhost:8080/books/1,控制台打印 “从数据库获取数据并缓存”。 - 再次访问同一接口,控制台打印 “从缓存获取数据”,说明缓存生效。
- 首次访问
- 更新测试:
- 调用
PUT http://localhost:8080/books更新数据,控制台显示 “删除缓存”。 - 再次查询,会重新从数据库获取并更新缓存。
- 调用
- 删除测试:
- 调用
DELETE http://localhost:8080/books/1,缓存被删除,再次查询会返回空(数据库记录已删除)。
- 调用
五、缓存注意事项
- 缓存一致性:更新 / 删除数据时需同步操作缓存(删除或更新),避免缓存与数据库数据不一致。
- 过期时间:设置缓存过期时间(如 30 分钟),防止缓存无限期存储导致内存溢出或数据过时。
- 缓存穿透:对不存在的 ID 查询,可缓存空值(短期),避免频繁访问数据库。
- 缓存雪崩:不同 key 设置随机过期时间,避免同时失效导致数据库压力骤增。

浙公网安备 33010602011771号