Spring Boot 緩存優(yōu)化Redis 實(shí)戰(zhàn)指南
當(dāng)你的Spring Boot項(xiàng)目逐步落地、用戶量攀升、數(shù)據(jù)量不斷增大后,你會(huì)發(fā)現(xiàn)一個(gè)明顯的問題:頻繁查詢數(shù)據(jù)庫(kù)會(huì)導(dǎo)致接口響應(yīng)變慢、數(shù)據(jù)庫(kù)壓力劇增,甚至出現(xiàn)卡頓、超時(shí)的情況——這既是項(xiàng)目發(fā)展的信號(hào),也是需要優(yōu)化的節(jié)點(diǎn)。緩存作為提升項(xiàng)目性能的核心手段,能有效減少數(shù)據(jù)庫(kù)查詢壓力,讓接口響應(yīng)速度翻倍,同時(shí)讓你的項(xiàng)目顯得更專業(yè)、更具競(jìng)爭(zhēng)力。本文承接項(xiàng)目數(shù)據(jù)量大后的優(yōu)化需求,手把手實(shí)戰(zhàn)Spring Boot集成Redis緩存,從環(huán)境搭建、核心注解使用,到熱點(diǎn)數(shù)據(jù)緩存的真實(shí)案例,代碼可直接復(fù)制復(fù)用,幫你快速落地緩存優(yōu)化,輕松解決數(shù)據(jù)量大帶來的性能瓶頸。
一、前置準(zhǔn)備:明確緩存核心與環(huán)境搭建
緩存的核心邏輯:將頻繁查詢、不常變化的數(shù)據(jù)(熱點(diǎn)數(shù)據(jù))存儲(chǔ)在Redis(內(nèi)存數(shù)據(jù)庫(kù))中,后續(xù)查詢時(shí)優(yōu)先從Redis獲取,避免頻繁訪問MySQL等關(guān)系型數(shù)據(jù)庫(kù),從而提升響應(yīng)速度、降低數(shù)據(jù)庫(kù)壓力。本次實(shí)戰(zhàn)基于Spring Boot + Redis + MyBatis-Plus,貼合真實(shí)項(xiàng)目架構(gòu),聚焦核心緩存功能落地。
1.1 環(huán)境準(zhǔn)備
- Redis環(huán)境:本地安裝Redis(Windows/Linux均可),確保Redis服務(wù)正常啟動(dòng)(默認(rèn)端口6379),無密碼或記住密碼(后續(xù)配置使用);
- 項(xiàng)目基礎(chǔ):已搭建Spring Boot項(xiàng)目,包含MyBatis-Plus(用于數(shù)據(jù)庫(kù)操作)、Web依賴(接口開發(fā)),確保數(shù)據(jù)庫(kù)連接正常。
1.2 Maven 核心依賴
Spring Boot提供了Redis緩存的自動(dòng)配置依賴,無需復(fù)雜配置,引入以下依賴即可快速集成Redis緩存:
<!-- Spring Boot Web 核心依賴 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring Boot Redis 緩存依賴(核心,自動(dòng)配置) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Spring Boot 緩存抽象依賴(提供緩存注解支持) -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- MyBatis-Plus 依賴(操作數(shù)據(jù)庫(kù)) -->
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
<version>3.5.3.1</version>
</dependency>
<!-- MySQL 驅(qū)動(dòng) -->
<dependency>
<groupId>com.mysql</groupId>
<artifactId>mysql-connector-j</artifactId>
<scope>runtime</scope>
</dependency>
<!-- lombok 簡(jiǎn)化代碼 -->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<!-- 可選:Redis 連接池依賴(提升Redis連接性能) -->
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-pool2</artifactId>
</dependency>1.3 核心配置(application.yml)
配置Redis連接信息、緩存相關(guān)參數(shù),以及緩存序列化方式(避免Redis中存儲(chǔ)的中文亂碼、對(duì)象無法反序列化),適配項(xiàng)目生產(chǎn)環(huán)境需求:
spring:
# 數(shù)據(jù)庫(kù)配置(替換為自己的數(shù)據(jù)庫(kù)信息)
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/springboot_redis?useUnicode=true&characterEncoding=utf-8&serverTimezone=GMT%2B8
username: root
password: 123456
# Redis 核心配置
redis:
host: localhost # Redis服務(wù)器地址(本地為localhost,遠(yuǎn)程為服務(wù)器IP)
port: 6379 # Redis默認(rèn)端口
password: # Redis密碼(無密碼則留空)
database: 0 # 操作的Redis數(shù)據(jù)庫(kù)(默認(rèn)0號(hào)庫(kù))
# Redis連接池配置(提升連接性能,推薦配置)
lettuce:
pool:
max-active: 16 # 最大連接數(shù)
max-idle: 8 # 最大空閑連接
min-idle: 4 # 最小空閑連接
max-wait: 1000 # 最大等待時(shí)間(毫秒)
# 緩存配置(開啟緩存,指定緩存類型為Redis)
cache:
type: redis # 緩存類型:Redis(默認(rèn)是Simple,本地內(nèi)存,不適用于生產(chǎn))
redis:
time-to-live: 3600000 # 緩存默認(rèn)過期時(shí)間(1小時(shí),單位:毫秒)
cache-null-values: false # 是否緩存null值(避免緩存穿透,根據(jù)需求調(diào)整)
key-prefix: springboot:cache: # 緩存key前綴(避免與其他Redis數(shù)據(jù)沖突)
use-key-prefix: true # 啟用key前綴
# MyBatis-Plus 配置(打印SQL,便于調(diào)試緩存效果)
mybatis-plus:
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
mapper-locations: classpath:mybatis/mapper/**/*.xml
type-aliases-package: com.example.demo.entity1.4 開啟緩存功能(核心注解)
在Spring Boot啟動(dòng)類上添加@EnableCaching注解,開啟緩存功能,一行代碼即可完成,簡(jiǎn)潔高效,是后續(xù)使用緩存注解的前提:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
/**
* 啟動(dòng)類:開啟緩存功能(@EnableCaching)
*/
@SpringBootApplication
@EnableCaching // 開啟緩存功能,必須添加,否則緩存注解無效
public class SpringBootRedisCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootRedisCacheApplication.class, args);
}
}關(guān)鍵注意點(diǎn):1. @EnableCaching必須添加在啟動(dòng)類或配置類上,否則@Cacheable、@CacheEvict等注解無法生效;2. 配置Redis緩存序列化方式(后續(xù)會(huì)補(bǔ)充),避免出現(xiàn)中文亂碼、對(duì)象反序列化失敗的問題;3. 確保Redis服務(wù)正常啟動(dòng),否則項(xiàng)目啟動(dòng)會(huì)報(bào)錯(cuò)。
1.5 補(bǔ)充:Redis緩存序列化配置(可選但推薦)
Spring Boot默認(rèn)的Redis緩存序列化方式會(huì)導(dǎo)致緩存的對(duì)象轉(zhuǎn)為二進(jìn)制,無法直接查看,且中文會(huì)亂碼。自定義序列化配置,將對(duì)象序列化為JSON格式,便于調(diào)試和查看,提升開發(fā)效率:
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.serializer.GenericJackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;
/**
* Redis緩存序列化配置(將對(duì)象序列化為JSON格式)
*/
@Configuration
@EnableCaching
public class RedisCacheConfig {
@Bean
public RedisCacheManager redisCacheManager(RedisConnectionFactory connectionFactory) {
// 1. 配置緩存序列化方式
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
// 設(shè)置緩存過期時(shí)間(全局默認(rèn),可在注解中單獨(dú)設(shè)置)
.entryTtl(Duration.ofHours(1))
// key序列化:String格式
.serializeKeysWith(RedisSerializationContext.SerializationPair
.fromSerializer(new StringRedisSerializer()))
// value序列化:JSON格式(支持對(duì)象序列化)
.serializeValuesWith(RedisSerializationContext.SerializationPair
.fromSerializer(new GenericJackson2JsonRedisSerializer()))
// 不緩存null值
.disableCachingNullValues()
// 啟用key前綴
.prefixCacheNameWith("springboot:cache:");
// 2. 創(chuàng)建緩存管理器并返回
return RedisCacheManager.builder(connectionFactory)
.cacheDefaults(config)
.build();
}
}二、核心實(shí)戰(zhàn)一:Redis 集成測(cè)試(確保環(huán)境可用)
集成Redis后,先編寫簡(jiǎn)單的測(cè)試接口,驗(yàn)證Redis連接正常、緩存功能可正常使用,為后續(xù)緩存注解和案例落地打下基礎(chǔ):
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
/**
* Redis 集成測(cè)試控制器
*/
@RestController
@RequestMapping("/redis/test")
public class RedisTestController {
// 注入Redis模板(StringRedisTemplate用于操作字符串,RedisTemplate用于操作對(duì)象)
@Autowired
private StringRedisTemplate stringRedisTemplate;
/**
* 測(cè)試Redis存儲(chǔ)字符串
*/
@GetMapping("/set")
public String setRedis(@RequestParam String key, @RequestParam String value) {
// 存儲(chǔ)數(shù)據(jù)到Redis
stringRedisTemplate.opsForValue().set(key, value);
return "Redis存儲(chǔ)成功,key:" + key + ",value:" + value;
}
/**
* 測(cè)試Redis獲取字符串
*/
@GetMapping("/get")
public String getRedis(@RequestParam String key) {
// 從Redis獲取數(shù)據(jù)
String value = stringRedisTemplate.opsForValue().get(key);
return "Redis獲取結(jié)果,key:" + key + ",value:" + (value == null ? "無此key" : value);
}
}測(cè)試步驟:
- 訪問接口:http://localhost:8080/redis/test/set?key=test&value=redis-cache,返回存儲(chǔ)成功;
- 訪問接口:http://localhost:8080/redis/test/get?key=test,返回獲取到的value;
- 打開Redis客戶端(如Redis Desktop Manager),可看到存儲(chǔ)的key-value(JSON格式,無亂碼),說明Redis集成成功。
三、核心實(shí)戰(zhàn)二:常用緩存注解(@Cacheable、@CacheEvict)
Spring Boot緩存抽象提供了便捷的注解,無需手動(dòng)編寫Redis操作代碼,僅通過注解即可實(shí)現(xiàn)緩存的增、刪、查,核心常用注解為@Cacheable(查詢緩存)和@CacheEvict(刪除緩存),覆蓋大多數(shù)緩存場(chǎng)景。
3.1 @Cacheable:查詢緩存(核心注解)
作用:在方法執(zhí)行前,先檢查Redis中是否有對(duì)應(yīng)緩存;若有,直接返回緩存數(shù)據(jù),不執(zhí)行方法;若沒有,執(zhí)行方法,將方法返回結(jié)果存入Redis,后續(xù)查詢直接復(fù)用緩存。
核心參數(shù)(常用):
- value/cacheNames:緩存名稱(必填),用于區(qū)分不同緩存模塊(如用戶緩存、商品緩存);
- key:緩存的key(可選),支持SpEL表達(dá)式,自定義緩存key,避免key沖突;
- unless:條件表達(dá)式(可選),滿足條件時(shí)不緩存(如返回值為null時(shí)不緩存);
- expire:緩存過期時(shí)間(可選),優(yōu)先級(jí)高于全局配置,單位:秒。
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.example.demo.entity.SysUser;
import com.example.demo.service.SysUserService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
* 緩存注解測(cè)試控制器(用戶模塊)
*/
@RestController
@RequestMapping("/user")
public class UserController {
@Autowired
private SysUserService sysUserService;
/**
* 示例1:根據(jù)用戶ID查詢用戶(緩存key為用戶ID)
* @Cacheable:查詢緩存,緩存名稱為user,key為用戶ID
*/
@Cacheable(value = "user", key = "#userId", unless = "#result == null")
@GetMapping("/{userId}")
public SysUser getUserById(@PathVariable Long userId) {
// 第一次查詢:無緩存,執(zhí)行方法,查詢數(shù)據(jù)庫(kù),存入緩存
// 第二次查詢:有緩存,直接返回緩存數(shù)據(jù),不執(zhí)行該方法(不打印SQL)
System.out.println("執(zhí)行數(shù)據(jù)庫(kù)查詢:根據(jù)ID查詢用戶,ID=" + userId);
return sysUserService.getById(userId);
}
/**
* 示例2:根據(jù)用戶名查詢用戶(自定義緩存key,添加過期時(shí)間)
* key:拼接緩存名稱和用戶名,避免key沖突;expire:緩存10分鐘
*/
@Cacheable(value = "user", key = "'username:'+#username", expire = 600)
@GetMapping("/username/{username}")
public SysUser getUserByUsername(@PathVariable String username) {
System.out.println("執(zhí)行數(shù)據(jù)庫(kù)查詢:根據(jù)用戶名查詢用戶,用戶名=" + username);
QueryWrapper<SysUser> queryWrapper = new QueryWrapper<>();
queryWrapper.eq("username", username);
return sysUserService.getOne(queryWrapper);
}
}測(cè)試效果:
- 第一次訪問http://localhost:8080/user/1,控制臺(tái)打印SQL(執(zhí)行數(shù)據(jù)庫(kù)查詢),Redis中新增緩存(key:springboot:cache:user::1);
- 第二次訪問同一個(gè)接口,控制臺(tái)不打印SQL(不執(zhí)行數(shù)據(jù)庫(kù)查詢),直接返回緩存數(shù)據(jù),接口響應(yīng)速度明顯提升;
- 訪問http://localhost:8080/user/username/test1,同理,第一次查詢走數(shù)據(jù)庫(kù),后續(xù)走緩存。
3.2 @CacheEvict:刪除緩存(核心注解)
作用:當(dāng)數(shù)據(jù)發(fā)生修改(更新、刪除)時(shí),刪除對(duì)應(yīng)的緩存,避免緩存與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致(緩存臟數(shù)據(jù))。
核心參數(shù)(常用):
- value/cacheNames:緩存名稱(必填),與@Cacheable的value一致;
- key:要?jiǎng)h除的緩存key(可選),與@Cacheable的key對(duì)應(yīng);
- allEntries:是否刪除該緩存名稱下的所有緩存(可選,默認(rèn)false);
- beforeInvocation:是否在方法執(zhí)行前刪除緩存(可選,默認(rèn)false,方法執(zhí)行后刪除)。
// 在UserController中新增更新、刪除接口,添加@CacheEvict注解
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
/**
* 示例3:更新用戶信息(刪除對(duì)應(yīng)緩存,避免臟數(shù)據(jù))
* @CacheEvict:刪除key為#user.id的緩存
*/
@CacheEvict(value = "user", key = "#user.id")
@PutMapping("/update")
public String updateUser(@RequestBody SysUser user) {
boolean success = sysUserService.updateById(user);
return success ? "用戶更新成功,緩存已刪除" : "用戶更新失敗";
}
/**
* 示例4:刪除用戶(刪除對(duì)應(yīng)緩存)
*/
@CacheEvict(value = "user", key = "#userId")
@DeleteMapping("/{userId}")
public String deleteUser(@PathVariable Long userId) {
boolean success = sysUserService.removeById(userId);
return success ? "用戶刪除成功,緩存已刪除" : "用戶刪除失敗";
}
/**
* 示例5:批量刪除用戶(刪除user緩存下的所有緩存)
* allEntries = true:刪除value="user"下的所有緩存
*/
@CacheEvict(value = "user", allEntries = true)
@DeleteMapping("/batchDelete")
public String batchDeleteUser(@RequestBody List<Long> userIds) {
boolean success = sysUserService.removeByIds(userIds);
return success ? "批量刪除成功,用戶緩存全部清空" : "批量刪除失敗";
}測(cè)試效果:
- 先訪問/user/1,緩存成功存入Redis;
- 訪問/update接口,更新ID為1的用戶信息,執(zhí)行完成后,Redis中key為springboot:cache:user::1的緩存被刪除;
- 再次訪問/user/1,控制臺(tái)打印SQL,重新查詢數(shù)據(jù)庫(kù)并緩存,確保緩存與數(shù)據(jù)庫(kù)數(shù)據(jù)一致。
四、核心實(shí)戰(zhàn)三:熱點(diǎn)數(shù)據(jù)緩存案例(落地項(xiàng)目)
項(xiàng)目中最適合用緩存的就是「熱點(diǎn)數(shù)據(jù)」——頻繁查詢、不常變化的數(shù)據(jù),如用戶信息、商品信息、字典數(shù)據(jù)等。本次以「商品列表+商品詳情」為案例,落地?zé)狳c(diǎn)數(shù)據(jù)緩存,模擬真實(shí)項(xiàng)目場(chǎng)景,讓緩存優(yōu)化真正發(fā)揮作用。
4.1 案例準(zhǔn)備:商品實(shí)體與Service層
// 1. 商品實(shí)體類(Product)
import com.baomidou.mybatisplus.annotation.IdType;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;
import java.math.BigDecimal;
import java.time.LocalDateTime;
@Data
@TableName("product")
public class Product {
@TableId(type = IdType.AUTO)
private Long id;
private String productName; // 商品名稱
private BigDecimal price; // 商品價(jià)格
private String description; // 商品描述
private Integer stock; // 庫(kù)存
private LocalDateTime createTime; // 創(chuàng)建時(shí)間
}
// 2. 商品Mapper接口
import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.example.demo.entity.Product;
import org.apache.ibatis.annotations.Mapper;
@Mapper
public interface ProductMapper extends BaseMapper<Product> {
}
// 3. 商品Service層
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.example.demo.entity.Product;
import com.example.demo.mapper.ProductMapper;
import org.springframework.stereotype.Service;
@Service
public class ProductService extends ServiceImpl<ProductMapper, Product> {
}4.2 熱點(diǎn)數(shù)據(jù)緩存實(shí)現(xiàn)(Controller)
商品列表(分頁)和商品詳情是典型的熱點(diǎn)數(shù)據(jù),頻繁被用戶訪問,且商品信息不會(huì)頻繁修改,適合緩存。結(jié)合@Cacheable、@CacheEvict實(shí)現(xiàn)緩存優(yōu)化:
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.example.demo.entity.Product;
import com.example.demo.service.ProductService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.web.bind.annotation.*;
import java.util.List;
/**
* 商品緩存案例控制器(熱點(diǎn)數(shù)據(jù)緩存)
*/
@RestController
@RequestMapping("/product")
public class ProductController {
@Autowired
private ProductService productService;
/**
* 熱點(diǎn)數(shù)據(jù)1:商品詳情(根據(jù)商品ID查詢)
* 緩存key:product:id:xxx,緩存過期時(shí)間30分鐘(1800秒)
* 說明:商品詳情訪問頻繁,且修改頻率低,適合長(zhǎng)期緩存
*/
@Cacheable(value = "product", key = "'id:'+#productId", expire = 1800)
@GetMapping("/{productId}")
public Product getProductById(@PathVariable Long productId) {
System.out.println("執(zhí)行數(shù)據(jù)庫(kù)查詢:查詢商品詳情,ID=" + productId);
return productService.getById(productId);
}
/**
* 熱點(diǎn)數(shù)據(jù)2:商品列表(分頁,無篩選條件)
* 緩存key:product:list:pageNum:xxx:pageSize:xxx
* 說明:首頁商品列表訪問量極高,緩存分頁數(shù)據(jù),減輕數(shù)據(jù)庫(kù)壓力
*/
@Cacheable(value = "product", key = "'list:pageNum:'+#pageNum+':pageSize:'+#pageSize")
@GetMapping("/list")
public IPage<Product> getProductList(
@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "10") Integer pageSize) {
System.out.println("執(zhí)行數(shù)據(jù)庫(kù)查詢:商品列表分頁,頁碼=" + pageNum + ",每頁條數(shù)=" + pageSize);
Page<Product> page = new Page<>(pageNum, pageSize);
QueryWrapper<Product> queryWrapper = new QueryWrapper<>();
queryWrapper.orderByDesc("create_time"); // 按創(chuàng)建時(shí)間降序
return productService.page(page, queryWrapper);
}
/**
* 商品新增(無需緩存,新增后無查詢緩存)
*/
@PostMapping("/add")
public String addProduct(@RequestBody Product product) {
boolean success = productService.save(product);
return success ? "商品新增成功" : "商品新增失敗";
}
/**
* 商品更新(刪除對(duì)應(yīng)商品詳情緩存和列表緩存)
* 說明:更新商品后,刪除該商品的詳情緩存和所有列表緩存,避免臟數(shù)據(jù)
*/
@CacheEvict(value = "product", allEntries = true)
@PutMapping("/update")
public String updateProduct(@RequestBody Product product) {
boolean success = productService.updateById(product);
return success ? "商品更新成功,所有商品緩存已清空" : "商品更新失敗";
}
/**
* 商品刪除(刪除對(duì)應(yīng)緩存)
*/
@CacheEvict(value = "product", key = "'id:'+#productId")
@DeleteMapping("/{productId}")
public String deleteProduct(@PathVariable Long productId) {
boolean success = productService.removeById(productId);
return success ? "商品刪除成功,對(duì)應(yīng)緩存已刪除" : "商品刪除失敗";
}
}4.3 案例測(cè)試與效果驗(yàn)證
- 訪問商品列表接口:http://localhost:8080/product/list?pageNum=1&pageSize=10,第一次執(zhí)行數(shù)據(jù)庫(kù)查詢(打印SQL),Redis中存入緩存;第二次訪問,直接返回緩存,響應(yīng)速度提升50%以上;
- 訪問商品詳情接口:http://localhost:8080/product/1,第一次查詢走數(shù)據(jù)庫(kù),后續(xù)走緩存;
- 執(zhí)行商品更新接口,更新ID為1的商品信息,執(zhí)行完成后,Redis中所有product緩存被清空;再次訪問列表和詳情接口,重新查詢數(shù)據(jù)庫(kù)并緩存;
- 查看Redis客戶端,可看到緩存的商品列表和詳情數(shù)據(jù)(JSON格式),過期時(shí)間正常生效。
案例優(yōu)化建議:1. 商品列表緩存可根據(jù)實(shí)際需求調(diào)整過期時(shí)間(如1小時(shí)),避免緩存數(shù)據(jù)過舊;2. 若商品列表有篩選條件(如按分類篩選),可在key中添加篩選參數(shù)(如key = “‘list:category:’+#categoryId+‘:pageNum:’+#pageNum”);3. 高并發(fā)場(chǎng)景下,可添加緩存預(yù)熱(項(xiàng)目啟動(dòng)時(shí)提前加載熱點(diǎn)數(shù)據(jù)到Redis)。
五、常見問題與解決方案(緩存落地避坑)
緩存注解不生效?
解決方案:① 檢查啟動(dòng)類是否添加@EnableCaching注解;② 注解標(biāo)注的方法必須是public方法,且不能是static、private方法;③ 注解標(biāo)注的類必須添加@Component(或@Controller、@Service),交給Spring管理;④ 避免在同一個(gè)類中調(diào)用標(biāo)注緩存注解的方法(Spring AOP代理機(jī)制限制)。
Redis緩存中文亂碼、對(duì)象無法反序列化?
解決方案:配置自定義緩存序列化方式(如本文1.5節(jié)的配置),使用GenericJackson2JsonRedisSerializer將對(duì)象序列化為JSON格式,避免默認(rèn)的二進(jìn)制序列化。
緩存與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致(臟數(shù)據(jù))?
解決方案:① 數(shù)據(jù)更新、刪除時(shí),必須使用@CacheEvict刪除對(duì)應(yīng)緩存;② 復(fù)雜場(chǎng)景(如多表關(guān)聯(lián)更新),可使用@CacheEvict(allEntries = true)清空對(duì)應(yīng)緩存;③ 避免緩存過期時(shí)間過長(zhǎng),合理設(shè)置過期時(shí)間。
緩存穿透(查詢不存在的數(shù)據(jù),頻繁訪問數(shù)據(jù)庫(kù))?
解決方案:① 配置cache-null-values: true,緩存null值(但需注意過期時(shí)間,避免緩存大量null值占用內(nèi)存);② 在接口層添加參數(shù)校驗(yàn),過濾無效參數(shù);③ 使用布隆過濾器,提前攔截不存在的key。
Redis連接失敗,項(xiàng)目啟動(dòng)報(bào)錯(cuò)?
解決方案:① 檢查Redis服務(wù)是否正常啟動(dòng);② 確認(rèn)Redis連接配置(host、port、password)正確;③ 檢查服務(wù)器防火墻是否開放Redis端口(6379);④ 若使用遠(yuǎn)程Redis,確認(rèn)遠(yuǎn)程連接權(quán)限開啟。
六、總結(jié)(緩存優(yōu)化提升項(xiàng)目專業(yè)度)
本文承接項(xiàng)目數(shù)據(jù)量大后的性能優(yōu)化需求,完整實(shí)戰(zhàn)了Spring Boot集成Redis緩存的核心流程,從環(huán)境搭建、緩存注解使用,到熱點(diǎn)數(shù)據(jù)緩存案例,幫你快速落地緩存優(yōu)化,核心要點(diǎn)總結(jié):
- 集成Redis:引入核心依賴,配置Redis連接信息和序列化方式,開啟緩存功能(@EnableCaching),確保Redis環(huán)境可用;
- 核心注解:@Cacheable實(shí)現(xiàn)查詢緩存,減少數(shù)據(jù)庫(kù)查詢;@CacheEvict實(shí)現(xiàn)緩存刪除,避免臟數(shù)據(jù),兩個(gè)注解覆蓋大多數(shù)緩存場(chǎng)景;
- 熱點(diǎn)緩存案例:以商品列表、商品詳情為核心,落地真實(shí)項(xiàng)目場(chǎng)景,讓緩存真正發(fā)揮性能優(yōu)化作用,提升接口響應(yīng)速度;
- 避坑要點(diǎn):解決緩存注解失效、中文亂碼、數(shù)據(jù)不一致等常見問題,確保緩存功能穩(wěn)定運(yùn)行,適配生產(chǎn)環(huán)境。
掌握本文內(nèi)容后,你可以輕松將Redis緩存集成到自己的項(xiàng)目中,解決數(shù)據(jù)量大帶來的性能瓶頸,讓接口響應(yīng)速度翻倍,同時(shí)降低數(shù)據(jù)庫(kù)壓力——這不僅能提升項(xiàng)目的實(shí)用性和用戶體驗(yàn),更能讓你的項(xiàng)目在同類產(chǎn)品中顯得更專業(yè)、更具競(jìng)爭(zhēng)力。后續(xù)可進(jìn)一步擴(kuò)展:緩存預(yù)熱、緩存降級(jí)、分布式緩存一致性等高級(jí)功能,讓緩存優(yōu)化更完善。
到此這篇關(guān)于Spring Boot 緩存優(yōu)化Redis 實(shí)戰(zhàn)指南的文章就介紹到這了,更多相關(guān)Spring Boot 緩存優(yōu)化內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- springboot項(xiàng)目redis緩存異常實(shí)戰(zhàn)案例詳解(提供解決方案)
- springboot整合ehcache和redis實(shí)現(xiàn)多級(jí)緩存實(shí)戰(zhàn)案例
- SpringBoot使用Cache集成Redis做緩存的保姆級(jí)教程
- SpringBoot使用注解集成Redis緩存的示例代碼
- SpringBoot整合Redis實(shí)現(xiàn)緩存分頁數(shù)據(jù)查詢功能
- 詳解SpringBoot如何使用Redis和Redis緩存
- SpringBoot使用Redis緩存MySql的方法步驟
- SpringBoot 開啟Redis緩存及使用方法
相關(guān)文章
Jenkins+Maven+SVN自動(dòng)化部署java項(xiàng)目
這篇文章主要介紹了Jenkins+Maven+SVN自動(dòng)化部署java項(xiàng)目,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-01-01
Java實(shí)戰(zhàn)之實(shí)現(xiàn)OA辦公管理系統(tǒng)
這篇文章主要介紹了如何通過Java實(shí)現(xiàn)OA辦公管理系統(tǒng),文章采用到了JSP、JQuery、Ajax等技術(shù),文中的示例代碼講解詳細(xì),感興趣的小伙伴可以了解一下2022-02-02
springboot整合curator實(shí)現(xiàn)分布式鎖過程
這篇文章主要介紹了springboot整合curator實(shí)現(xiàn)分布式鎖過程,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-05-05
詳解Java中的時(shí)區(qū)類TimeZone的用法
TimeZone可以用來獲取或者規(guī)定時(shí)區(qū),也可以用來計(jì)算時(shí)差,這里我們就來詳解Java中的時(shí)區(qū)類TimeZone的用法,特別要注意下面所提到的TimeZone相關(guān)的時(shí)間校準(zhǔn)問題.2016-06-06
Java 9中如何對(duì)IntegerCache進(jìn)行修改詳解
這篇文章主要給大家介紹了關(guān)于Java 9中如何對(duì)IntegerCache進(jìn)行修改的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或使用java9具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起看看吧。2017-12-12
Java中使用增強(qiáng)for循環(huán)的實(shí)例方法
在本篇文章里小編給大家整理是的關(guān)于Java中如何使用增強(qiáng)for循環(huán)的實(shí)例內(nèi)容以及相關(guān)代碼,需要的朋友們可以學(xué)習(xí)下。2019-08-08
Java Stream 的 limit 與 skip 使用場(chǎng)
Java Stream的limit與skip操作用于控制元素?cái)?shù)量,limit截取前N個(gè)元素,具備短路特性,適合無限流和性能優(yōu)化,skip跳過前N個(gè)元素,常用于分頁,兩者結(jié)合可實(shí)現(xiàn)數(shù)據(jù)切片,但需注意順序依賴性和性能陷阱,本文介紹Java Stream的limit與skip使用場(chǎng)景操作分析,感興趣的朋友一起看看吧2025-07-07
SpringBoot使用Redis緩存的實(shí)現(xiàn)方法
這篇文章主要介紹了SpringBoot使用Redis緩存的實(shí)現(xiàn)方法,需要的朋友可以參考下2018-02-02

