当前位置: 首页 > news >正文

Spring Boot缓存机制详解

文章目录

    • 1. 缓存概述
      • 1.1 缓存类型
      • 1.2 缓存策略
      • 1.3 核心依赖
    • 2. Spring Cache注解
      • 2.1 基础缓存注解
      • 2.2 条件缓存
    • 3. 缓存配置
      • 3.1 基础缓存配置
      • 3.2 Caffeine缓存配置
      • 3.3 Redis缓存配置
    • 4. 多级缓存
      • 4.1 多级缓存实现
      • 4.2 缓存预热
    • 5. 缓存监控
      • 5.1 缓存统计
      • 5.2 缓存监控端点
    • 6. 缓存策略
      • 6.1 缓存更新策略
      • 6.2 缓存一致性
    • 7. 缓存性能优化
      • 7.1 缓存预热
      • 7.2 缓存压缩
    • 8. 缓存配置优化
      • 8.1 配置文件
      • 8.2 高级缓存配置
    • 9. 总结

1. 缓存概述

缓存是提高应用性能的重要手段,Spring Boot提供了完整的缓存解决方案。通过缓存可以减少数据库访问、提高响应速度、降低系统负载。

1.1 缓存类型

  • 本地缓存:JVM内存中的缓存,速度快但容量有限
  • 分布式缓存:Redis、Memcached等,支持集群部署
  • 数据库缓存:查询结果缓存、连接池缓存
  • HTTP缓存:浏览器缓存、CDN缓存

1.2 缓存策略

  • Cache-Aside:应用程序管理缓存
  • Read-Through:缓存自动从数据源读取
  • Write-Through:同时写入缓存和数据源
  • Write-Behind:异步写入数据源

1.3 核心依赖

<dependencies><!-- Spring Boot Cache --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!-- Spring Boot Data Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Caffeine Cache --><dependency><groupId>com.github.ben-manes.caffeine</groupId><artifactId>caffeine</artifactId></dependency><!-- EhCache --><dependency><groupId>org.ehcache</groupId><artifactId>ehcache</artifactId></dependency>
</dependencies>

2. Spring Cache注解

2.1 基础缓存注解

package com.example.demo.service;import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.Optional;@Service
public class UserCacheService {@Autowiredprivate UserRepository userRepository;// 缓存查询结果@Cacheable(value = "users", key = "#id")public Optional<User> findById(Long id) {System.out.println("从数据库查询用户: " + id);return userRepository.findById(id);}// 缓存用户列表@Cacheable(value = "userList", key = "#status")public List<User> findByStatus(String status) {System.out.println("从数据库查询用户列表: " + status);return userRepository.findByStatus(status);}// 更新缓存@CachePut(value = "users", key = "#user.id")public User save(User user) {System.out.println("保存用户到数据库: " + user.getUsername());return userRepository.save(user);}// 清除缓存@CacheEvict(value = "users", key = "#id")public void deleteById(Long id) {System.out.println("从数据库删除用户: " + id);userRepository.deleteById(id);}// 清除所有用户缓存@CacheEvict(value = "users", allEntries = true)public void clearAllUserCache() {System.out.println("清除所有用户缓存");}// 组合缓存操作@Caching(evict = {@CacheEvict(value = "users", key = "#user.id"),@CacheEvict(value = "userList", allEntries = true)})public User updateUser(User user) {System.out.println("更新用户: " + user.getUsername());return userRepository.save(user);}
}

2.2 条件缓存

package com.example.demo.service;import com.example.demo.entity.User;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;@Service
public class ConditionalCacheService {// 条件缓存:只有活跃用户才缓存@Cacheable(value = "activeUsers", condition = "#user.status == 'ACTIVE'")public User cacheActiveUser(User user) {System.out.println("缓存活跃用户: " + user.getUsername());return user;}// 条件清除:只有特定状态才清除@CacheEvict(value = "users", condition = "#user.status == 'INACTIVE'")public void evictInactiveUser(User user) {System.out.println("清除非活跃用户缓存: " + user.getUsername());}// 条件更新:只有特定条件才更新缓存@CachePut(value = "users", unless = "#result == null")public User updateUserConditionally(User user) {if (user.getId() == null) {return null; // 不缓存null结果}return user;}
}

3. 缓存配置

3.1 基础缓存配置

package com.example.demo.config;import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
@EnableCaching
public class CacheConfig {@Beanpublic CacheManager cacheManager() {ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();cacheManager.setCacheNames("users", "userList", "activeUsers");return cacheManager;}
}

3.2 Caffeine缓存配置

package com.example.demo.config;import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import java.util.concurrent.TimeUnit;@Configuration
@EnableCaching
public class CaffeineCacheConfig {@Beanpublic CacheManager cacheManager() {CaffeineCacheManager cacheManager = new CaffeineCacheManager();cacheManager.setCaffeine(Caffeine.newBuilder().maximumSize(1000).expireAfterWrite(10, TimeUnit.MINUTES).expireAfterAccess(5, TimeUnit.MINUTES).recordStats());return cacheManager;}
}

3.3 Redis缓存配置

package com.example.demo.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.jsontype.impl.LaissezFaireSubTypeValidator;
import org.springframework.cache.CacheManager;
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.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;
import org.springframework.data.redis.serializer.StringRedisSerializer;
import java.time.Duration;@Configuration
public class RedisCacheConfig {@Beanpublic CacheManager cacheManager(RedisConnectionFactory connectionFactory) {// 配置序列化Jackson2JsonRedisSerializer<Object> serializer = new Jackson2JsonRedisSerializer<>(Object.class);ObjectMapper mapper = new ObjectMapper();mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);mapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL);serializer.setObjectMapper(mapper);// 配置缓存RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(serializer)).disableCachingNullValues();return RedisCacheManager.builder(connectionFactory).cacheDefaults(config).build();}
}

4. 多级缓存

4.1 多级缓存实现

package com.example.demo.service;import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.Optional;
import java.util.concurrent.TimeUnit;@Service
public class MultiLevelCacheService {@Autowiredprivate UserRepository userRepository;@Autowiredprivate RedisTemplate<String, Object> redisTemplate;private static final String REDIS_KEY_PREFIX = "user:";private static final long REDIS_EXPIRE_TIME = 30; // 30分钟// L1缓存:本地缓存@Cacheable(value = "localUsers", key = "#id")public Optional<User> getUserFromL1Cache(Long id) {System.out.println("L1缓存未命中,查询L2缓存");return getUserFromL2Cache(id);}// L2缓存:Redis缓存public Optional<User> getUserFromL2Cache(Long id) {String redisKey = REDIS_KEY_PREFIX + id;User user = (User) redisTemplate.opsForValue().get(redisKey);if (user != null) {System.out.println("L2缓存命中: " + id);return Optional.of(user);}System.out.println("L2缓存未命中,查询数据库");return getUserFromDatabase(id);}// L3缓存:数据库public Optional<User> getUserFromDatabase(Long id) {System.out.println("从数据库查询用户: " + id);Optional<User> user = userRepository.findById(id);if (user.isPresent()) {// 写入L2缓存String redisKey = REDIS_KEY_PREFIX + id;redisTemplate.opsForValue().set(redisKey, user.get(), REDIS_EXPIRE_TIME, TimeUnit.MINUTES);}return user;}
}

4.2 缓存预热

package com.example.demo.service;import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import javax.annotation.PostConstruct;
import java.util.List;@Service
public class CacheWarmupService {@Autowiredprivate UserRepository userRepository;@Autowiredprivate UserCacheService userCacheService;@PostConstructpublic void warmupCache() {System.out.println("开始缓存预热...");// 预热活跃用户缓存List<User> activeUsers = userRepository.findByStatus("ACTIVE");for (User user : activeUsers) {userCacheService.findById(user.getId());}System.out.println("缓存预热完成,预热了 " + activeUsers.size() + " 个用户");}
}

5. 缓存监控

5.1 缓存统计

package com.example.demo.service;import com.github.benmanes.caffeine.cache.Cache;
import com.github.benmanes.caffeine.cache.stats.CacheStats;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.CacheManager;
import org.springframework.cache.caffeine.CaffeineCache;
import org.springframework.stereotype.Service;
import java.util.HashMap;
import java.util.Map;@Service
public class CacheStatsService {@Autowiredprivate CacheManager cacheManager;public Map<String, Object> getCacheStats() {Map<String, Object> stats = new HashMap<>();cacheManager.getCacheNames().forEach(cacheName -> {CaffeineCache caffeineCache = (CaffeineCache) cacheManager.getCache(cacheName);if (caffeineCache != null) {Cache<Object, Object> nativeCache = caffeineCache.getNativeCache();CacheStats cacheStats = nativeCache.stats();Map<String, Object> cacheStatsMap = new HashMap<>();cacheStatsMap.put("hitCount", cacheStats.hitCount());cacheStatsMap.put("missCount", cacheStats.missCount());cacheStatsMap.put("hitRate", cacheStats.hitRate());cacheStatsMap.put("evictionCount", cacheStats.evictionCount());cacheStatsMap.put("size", nativeCache.estimatedSize());stats.put(cacheName, cacheStatsMap);}});return stats;}
}

5.2 缓存监控端点

package com.example.demo.controller;import com.example.demo.service.CacheStatsService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.Map;@RestController
@RequestMapping("/api/cache")
public class CacheController {@Autowiredprivate CacheStatsService cacheStatsService;@GetMapping("/stats")public Map<String, Object> getCacheStats() {return cacheStatsService.getCacheStats();}
}

6. 缓存策略

6.1 缓存更新策略

package com.example.demo.service;import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class CacheUpdateStrategyService {@Autowiredprivate UserRepository userRepository;// 写回策略:同时更新缓存和数据库@CachePut(value = "users", key = "#user.id")@Transactionalpublic User writeThrough(User user) {System.out.println("写回策略:同时更新缓存和数据库");return userRepository.save(user);}// 写分配策略:先更新数据库,再更新缓存@Transactionalpublic User writeAllocate(User user) {System.out.println("写分配策略:先更新数据库");User savedUser = userRepository.save(user);// 手动更新缓存updateCache(savedUser);return savedUser;}// 写不分配策略:只更新数据库,清除缓存@CacheEvict(value = "users", key = "#user.id")@Transactionalpublic User writeNoAllocate(User user) {System.out.println("写不分配策略:只更新数据库,清除缓存");return userRepository.save(user);}// 异步写回策略@Transactionalpublic User writeBehind(User user) {System.out.println("异步写回策略:先更新缓存,异步更新数据库");updateCache(user);// 异步更新数据库asyncUpdateDatabase(user);return user;}private void updateCache(User user) {// 实现缓存更新逻辑}private void asyncUpdateDatabase(User user) {// 实现异步数据库更新逻辑}
}

6.2 缓存一致性

package com.example.demo.service;import com.example.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.util.concurrent.TimeUnit;@Service
public class CacheConsistencyService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 强一致性:使用分布式锁public User updateUserWithLock(User user) {String lockKey = "lock:user:" + user.getId();String lockValue = String.valueOf(System.currentTimeMillis());try {// 获取分布式锁Boolean lockAcquired = redisTemplate.opsForValue().setIfAbsent(lockKey, lockValue, 30, TimeUnit.SECONDS);if (lockAcquired) {// 更新数据库updateDatabase(user);// 更新缓存updateCache(user);return user;} else {throw new RuntimeException("获取锁失败");}} finally {// 释放锁releaseLock(lockKey, lockValue);}}// 最终一致性:使用消息队列public User updateUserWithMQ(User user) {// 更新数据库updateDatabase(user);// 发送缓存更新消息sendCacheUpdateMessage(user);return user;}private void updateDatabase(User user) {// 实现数据库更新逻辑}private void updateCache(User user) {// 实现缓存更新逻辑}private void sendCacheUpdateMessage(User user) {// 实现消息发送逻辑}private void releaseLock(String lockKey, String lockValue) {// 实现锁释放逻辑}
}

7. 缓存性能优化

7.1 缓存预热

package com.example.demo.service;import com.example.demo.entity.User;
import com.example.demo.repository.UserRepository;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.CompletableFuture;@Service
public class CacheWarmupService {@Autowiredprivate UserRepository userRepository;@Autowiredprivate UserCacheService userCacheService;// 异步预热缓存@Asyncpublic CompletableFuture<Void> warmupCacheAsync() {System.out.println("开始异步缓存预热...");List<User> users = userRepository.findAll();for (User user : users) {userCacheService.findById(user.getId());}System.out.println("异步缓存预热完成");return CompletableFuture.completedFuture(null);}// 分批预热缓存public void warmupCacheInBatches(int batchSize) {System.out.println("开始分批缓存预热...");List<User> users = userRepository.findAll();for (int i = 0; i < users.size(); i += batchSize) {int endIndex = Math.min(i + batchSize, users.size());List<User> batch = users.subList(i, endIndex);for (User user : batch) {userCacheService.findById(user.getId());}System.out.println("预热批次 " + (i / batchSize + 1) + " 完成");}System.out.println("分批缓存预热完成");}
}

7.2 缓存压缩

package com.example.demo.service;import com.example.demo.entity.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;@Service
public class CacheCompressionService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;// 压缩存储public void storeCompressed(String key, User user) {try {byte[] compressed = compress(user);redisTemplate.opsForValue().set(key, compressed);} catch (Exception e) {throw new RuntimeException("压缩存储失败", e);}}// 解压读取public User getCompressed(String key) {try {byte[] compressed = (byte[]) redisTemplate.opsForValue().get(key);if (compressed != null) {return decompress(compressed);}return null;} catch (Exception e) {throw new RuntimeException("解压读取失败", e);}}private byte[] compress(User user) throws Exception {ByteArrayOutputStream baos = new ByteArrayOutputStream();GZIPOutputStream gzos = new GZIPOutputStream(baos);ObjectOutputStream oos = new ObjectOutputStream(gzos);oos.writeObject(user);oos.close();return baos.toByteArray();}private User decompress(byte[] compressed) throws Exception {ByteArrayInputStream bais = new ByteArrayInputStream(compressed);GZIPInputStream gzis = new GZIPInputStream(bais);ObjectInputStream ois = new ObjectInputStream(gzis);User user = (User) ois.readObject();ois.close();return user;}
}

8. 缓存配置优化

8.1 配置文件

# application.yml
spring:cache:type: caffeinecaffeine:spec: maximumSize=1000,expireAfterWrite=10m,expireAfterAccess=5mredis:time-to-live: 600000 # 10分钟cache-null-values: falseuse-key-prefix: truekey-prefix: "cache:"redis:host: localhostport: 6379password: database: 0timeout: 2000mslettuce:pool:max-active: 8max-idle: 8min-idle: 0max-wait: -1ms# 缓存监控
management:endpoints:web:exposure:include: cache,health,metricsendpoint:cache:enabled: truemetrics:export:prometheus:enabled: true

8.2 高级缓存配置

package com.example.demo.config;import com.github.benmanes.caffeine.cache.Caffeine;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.caffeine.CaffeineCacheManager;
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;
import java.util.HashMap;
import java.util.Map;@Configuration
@EnableCaching
public class AdvancedCacheConfig {@Beanpublic CacheManager cacheManager(RedisConnectionFactory connectionFactory) {// 配置不同的缓存策略Map<String, RedisCacheConfiguration> cacheConfigurations = new HashMap<>();// 用户缓存:10分钟过期cacheConfigurations.put("users", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));// 用户列表缓存:5分钟过期cacheConfigurations.put("userList", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));// 活跃用户缓存:30分钟过期cacheConfigurations.put("activeUsers", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())));return RedisCacheManager.builder(connectionFactory).cacheDefaults(RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(10)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))).withInitialCacheConfigurations(cacheConfigurations).build();}
}

9. 总结

Spring Boot缓存机制提供了完整的缓存解决方案:

  1. 缓存注解:@Cacheable、@CachePut、@CacheEvict等
  2. 缓存配置:支持多种缓存实现(Caffeine、Redis、EhCache)
  3. 多级缓存:本地缓存+分布式缓存
  4. 缓存监控:缓存统计和性能监控
  5. 缓存策略:多种缓存更新策略
  6. 性能优化:缓存预热、压缩、异步处理
  7. 配置优化:灵活的缓存配置

通过合理使用这些缓存特性,可以显著提高应用性能和用户体验。

http://www.dtcms.com/a/479412.html

相关文章:

  • 做照片的网站有哪些软件小程序api接口怎么对接
  • 为Windows10配置“一键睡眠”的方法
  • 云建站不能用了吗英文网站设计哪家好
  • 青海建设信息网站网站开发专业分析
  • 怎么给搞笑网站做文案网站安全建设
  • 网站建设及解决方案房地产公司 网站建设
  • 【升级Cli5】记一次vue2由cli4升级到cli5的实际操作
  • 【深度学习理论基础】马尔可夫链
  • 利用python做网站用ps做网站页面的大小
  • 阿里云免费建站最新网页游戏公益服
  • 飞控信号模块技术要点与难点分析
  • PHP 变量
  • Java 大视界 -- Java 大数据中的数据隐私保护技术在多方数据协作中的应用
  • 打开网站8秒原则做门户网站的系统
  • 基于spark的抖音短视频数据分析及可视化
  • wordpress导航网站模板邢台网站建设好蜘蛛
  • 欧美(美股、加拿大股票、墨西哥股票)股票数据接口文档
  • 做网站的分析报告案例网站用cms
  • 四川省建设厅官方培训网站江苏建设教育网官网入口
  • 国内永久免费crm系统网站推荐有哪些网页设计作业讲解
  • 上海免费网站建设服务广告推广平台哪个好
  • 深圳市龙岗区住房和建设局官方网站莱西建设局官方网站
  • 彩票系统网站开发自己做的网站如何链接到百度
  • langsmith进行agent评估的方法
  • 手机微信网站怎么做的百度js转wordpress
  • 网站开发报价范围城乡企业建设部网站
  • 9、C/C++ 内存管理详解:从基础到面试题
  • 筑巢网站建设怎么样建站工具介绍
  • 为什么自己做的网站打开是乱码效果图网站有哪些
  • 分布式计算框架:从批处理到流处理的演进