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

Spring Boot 缓存优化:从入门到精通

Spring Boot 缓存优化:从入门到精通

核心概念

缓存是提高应用性能的重要手段,Spring Boot 提供了强大的缓存支持。通过合理配置和使用缓存,可以显著减少数据库访问次数,提高响应速度。

Spring Boot 缓存抽象

Spring Boot 提供了统一的缓存抽象层,支持多种缓存实现:

  1. ConcurrentHashMap:默认缓存实现,适合开发环境
  2. Redis:分布式缓存,适合生产环境
  3. Caffeine:高性能本地缓存
  4. EhCache:成熟的缓存解决方案

缓存配置

// 缓存配置类 @Configuration @EnableCaching public class CacheConfig { @Bean public CacheManager cacheManager() { SimpleCacheManager cacheManager = new SimpleCacheManager(); List<Cache> caches = Arrays.asList( new ConcurrentMapCache("users"), new ConcurrentMapCache("products"), new ConcurrentMapCache("orders") ); cacheManager.setCaches(caches); return cacheManager; } } // 使用 Redis 作为缓存 @Configuration @EnableCaching public class RedisCacheConfig { @Bean public CacheManager cacheManager(RedisConnectionFactory connectionFactory) { RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig() .entryTtl(Duration.ofMinutes(30)) .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())) .serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer())); return RedisCacheManager.builder(connectionFactory) .cacheDefaults(config) .build(); } } // 使用 Caffeine 作为缓存 @Configuration @EnableCaching public class CaffeineCacheConfig { @Bean public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); cacheManager.setCaffeine(Caffeine.newBuilder() .initialCapacity(100) .maximumSize(500) .expireAfterWrite(Duration.ofMinutes(30)) .recordStats()); cacheManager.setCacheNames(Arrays.asList("users", "products", "orders")); return cacheManager; } }

缓存注解使用

// 缓存服务示例 @Service public class UserService { private final UserRepository userRepository; public UserService(UserRepository userRepository) { this.userRepository = userRepository; } @Cacheable(value = "users", key = "#id") public User findById(Long id) { // 只有第一次调用时会执行此方法,后续调用直接从缓存获取 return userRepository.findById(id).orElse(null); } @Cacheable(value = "users", key = "#email") public User findByEmail(String email) { return userRepository.findByEmail(email).orElse(null); } @CachePut(value = "users", key = "#user.id") public User save(User user) { // 更新缓存 return userRepository.save(user); } @CacheEvict(value = "users", key = "#id") public void deleteById(Long id) { // 删除缓存 userRepository.deleteById(id); } @CacheEvict(value = "users", allEntries = true) public void clearCache() { // 清除所有用户缓存 } @Caching( put = { @CachePut(value = "users", key = "#user.id"), @CachePut(value = "users", key = "#user.email") } ) public User update(User user) { // 更新多个缓存条目 return userRepository.save(user); } }

缓存条件

@Service public class ProductService { private final ProductRepository productRepository; public ProductService(ProductRepository productRepository) { this.productRepository = productRepository; } @Cacheable(value = "products", key = "#id", condition = "#id > 0") public Product findById(Long id) { // 只有 id > 0 时才使用缓存 return productRepository.findById(id).orElse(null); } @Cacheable(value = "products", key = "#name", unless = "#result == null") public Product findByName(String name) { // 只有结果不为 null 时才缓存 return productRepository.findByName(name).orElse(null); } @Cacheable(value = "products", key = "#category", condition = "#category != 'ALL'") public List<Product> findByCategory(String category) { // 只有 category 不是 'ALL' 时才使用缓存 return productRepository.findByCategory(category); } }

缓存与事务

@Service @Transactional public class OrderService { private final OrderRepository orderRepository; private final ProductService productService; public OrderService(OrderRepository orderRepository, ProductService productService) { this.orderRepository = orderRepository; this.productService = productService; } @Cacheable(value = "orders", key = "#id") public Order findById(Long id) { return orderRepository.findById(id).orElse(null); } @CachePut(value = "orders", key = "#order.id") public Order create(Order order) { // 在事务内创建订单,缓存会在事务提交后更新 return orderRepository.save(order); } @CacheEvict(value = "orders", key = "#id") public void cancel(Long id) { Order order = findById(id); if (order != null) { order.setStatus("CANCELLED"); orderRepository.save(order); } } }

缓存穿透解决方案

// 缓存穿透:查询不存在的数据 @Service public class CachePenetrationService { private static final String NULL_VALUE = "NULL_VALUE"; @Autowired private UserRepository userRepository; @Cacheable(value = "users", key = "#id") public User findById(Long id) { User user = userRepository.findById(id).orElse(null); if (user == null) { // 返回一个特殊的空值标记,防止缓存穿透 throw new UserNotFoundException("User not found"); } return user; } } // 使用布隆过滤器防止缓存穿透 @Component public class BloomFilterCache { private final BloomFilter<Long> userBloomFilter; public BloomFilterCache() { // 预计插入 1000000 条数据,误判率 0.01% this.userBloomFilter = BloomFilter.create( Funnels.longFunnel(), 1000000, 0.0001 ); } public void add(Long id) { userBloomFilter.put(id); } public boolean mightContain(Long id) { return userBloomFilter.mightContain(id); } } @Service public class UserServiceWithBloomFilter { private final BloomFilterCache bloomFilterCache; private final UserRepository userRepository; public UserServiceWithBloomFilter(BloomFilterCache bloomFilterCache, UserRepository userRepository) { this.bloomFilterCache = bloomFilterCache; this.userRepository = userRepository; } @Cacheable(value = "users", key = "#id") public User findById(Long id) { // 先检查布隆过滤器 if (!bloomFilterCache.mightContain(id)) { throw new UserNotFoundException("User not found"); } return userRepository.findById(id).orElseThrow(() -> new UserNotFoundException("User not found")); } }

缓存击穿解决方案

// 缓存击穿:热点数据过期时大量请求同时访问数据库 @Service public class HotProductService { private final ProductRepository productRepository; private final ReentrantLock lock = new ReentrantLock(); @Cacheable(value = "products", key = "#id") public Product getHotProduct(Long id) { return getProductFromDb(id); } private Product getProductFromDb(Long id) { // 使用双重检查锁防止缓存击穿 String cacheKey = "products::" + id; // 尝试获取锁 if (lock.tryLock()) { try { // 再次检查缓存 Product cached = getFromCache(cacheKey); if (cached != null) { return cached; } // 从数据库获取 Product product = productRepository.findById(id).orElse(null); // 更新缓存 if (product != null) { updateCache(cacheKey, product); } return product; } finally { lock.unlock(); } } else { // 等待其他线程更新缓存 try { Thread.sleep(100); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // 再次尝试从缓存获取 return getHotProduct(id); } } private Product getFromCache(String key) { // 从缓存获取 return null; } private void updateCache(String key, Product product) { // 更新缓存 } }

缓存雪崩解决方案

// 缓存雪崩:大量缓存同时过期 @Configuration @EnableCaching public class CacheAvalancheConfig { @Bean public CacheManager cacheManager() { CaffeineCacheManager cacheManager = new CaffeineCacheManager(); cacheManager.setCaffeine(Caffeine.newBuilder() .initialCapacity(100) .maximumSize(500) .expireAfterWrite(Duration.ofMinutes(30)) .expireAfterAccess(Duration.ofMinutes(10)) .recordStats()); return cacheManager; } } @Service public class ProductServiceWithRandomExpire { private final ProductRepository productRepository; public ProductServiceWithRandomExpire(ProductRepository productRepository) { this.productRepository = productRepository; } @Cacheable(value = "products", key = "#id") public Product findById(Long id) { Product product = productRepository.findById(id).orElse(null); if (product != null) { // 设置随机过期时间,避免缓存雪崩 int randomMinutes = new Random().nextInt(10) + 25; // 25-35 分钟 updateCacheWithExpire("products::" + id, product, randomMinutes); } return product; } private void updateCacheWithExpire(String key, Product product, int minutes) { // 更新缓存并设置过期时间 } }

缓存监控

// 缓存监控服务 @Service public class CacheMetricsService { private final CacheManager cacheManager; public CacheMetricsService(CacheManager cacheManager) { this.cacheManager = cacheManager; } public Map<String, CacheStats> getCacheStats() { Map<String, CacheStats> stats = new HashMap<>(); cacheManager.getCacheNames().forEach(cacheName -> { Cache cache = cacheManager.getCache(cacheName); if (cache instanceof CaffeineCache) { CaffeineCache caffeineCache = (CaffeineCache) cache; com.github.benmanes.caffeine.cache.Cache<Object, Object> nativeCache = caffeineCache.getNativeCache(); CacheStats cacheStats = nativeCache.stats(); stats.put(cacheName, cacheStats); } }); return stats; } public void printStats() { Map<String, CacheStats> stats = getCacheStats(); stats.forEach((cacheName, cacheStats) -> { System.out.println("Cache: " + cacheName); System.out.println(" Hits: " + cacheStats.hitCount()); System.out.println(" Misses: " + cacheStats.missCount()); System.out.println(" Hit Rate: " + String.format("%.2f%%", cacheStats.hitRate() * 100)); System.out.println(" Evictions: " + cacheStats.evictionCount()); System.out.println(); }); } } // 缓存统计端点 @RestController @RequestMapping("/cache") public class CacheController { private final CacheMetricsService cacheMetricsService; public CacheController(CacheMetricsService cacheMetricsService) { this.cacheMetricsService = cacheMetricsService; } @GetMapping("/stats") public ResponseEntity<Map<String, Object>> getCacheStats() { Map<String, Object> result = new HashMap<>(); Map<String, CacheStats> stats = cacheMetricsService.getCacheStats(); stats.forEach((cacheName, cacheStats) -> { Map<String, Object> cacheInfo = new HashMap<>(); cacheInfo.put("hits", cacheStats.hitCount()); cacheInfo.put("misses", cacheStats.missCount()); cacheInfo.put("hitRate", String.format("%.2f%%", cacheStats.hitRate() * 100)); cacheInfo.put("evictions", cacheStats.evictionCount()); cacheInfo.put("averageLoadPenalty", cacheStats.averageLoadPenalty()); result.put(cacheName, cacheInfo); }); return ResponseEntity.ok(result); } @DeleteMapping("/clear") public ResponseEntity<Void> clearAllCache() { cacheMetricsService.getCacheStats().keySet().forEach(cacheName -> { Cache cache = cacheMetricsService.cacheManager.getCache(cacheName); if (cache != null) { cache.clear(); } }); return ResponseEntity.noContent().build(); } }

最佳实践

  1. 选择合适的缓存策略:根据业务场景选择本地缓存或分布式缓存
  2. 设置合理的过期时间:根据数据更新频率设置过期时间
  3. 处理缓存与数据库一致性:使用 @CachePut 更新缓存
  4. 防止缓存穿透:使用布隆过滤器或空值缓存
  5. 防止缓存击穿:使用互斥锁或分布式锁
  6. 防止缓存雪崩:设置随机过期时间
  7. 监控缓存性能:定期检查缓存命中率
  8. 合理规划缓存键:使用有意义的缓存键命名规范

实际应用场景

  • 高频查询场景:如商品详情、用户信息
  • 热点数据缓存:如首页推荐、热门商品
  • 会话缓存:如用户登录状态
  • 计算结果缓存:如复杂查询结果

总结

Spring Boot 的缓存抽象层提供了强大而灵活的缓存支持。通过合理配置和使用缓存,可以显著提高应用性能。在实际应用中,需要根据业务场景选择合适的缓存策略,并注意处理缓存穿透、击穿和雪崩等问题。

别叫我大神,叫我 Alex 就好。这其实可以更优雅一点,合理的缓存配置让应用性能变得更加出色和高效。

http://www.jsqmd.com/news/787864/

相关文章:

  • 5G波形技术演进与新型解决方案对比
  • 钉钉机器人 Webhook 方式与 SDK 方式接入哪种更适合 CI/CD 场景?
  • 2026年四川地区钢材采购决策:如何筛选靠谱供应商与盛世钢联建立长期合作 - 四川盛世钢联营销中心
  • Arm安全协处理器寄存器架构与内存重映射技术解析
  • 2026粉末冶金加工厂家推荐:铜基与铁基粉末冶金厂家的工艺特点及应用领域 - 栗子测评
  • 使用HermesAgent工具连接Taotoken为自动化测试脚本添加智能分析步骤
  • 测试工程师年度成长清单:每月一个小目标,年底大变样
  • CANN/opbase:设置动态输出张量地址
  • CANN/ATVC PyTorch调用样例
  • 技术人副业指南:8种低门槛、高天花板的变现方式
  • PyTorch 张量操作优化:内存布局与计算效率
  • 杰理之打开OTA打印以及uboot打印的方法【篇】
  • 基于开源模板快速构建AI编程助手:从智能体原理到实战应用
  • Windows热键冲突终极解决方案:Hotkey Detective专业指南
  • 深度学习在计算病理学中的应用:从WSI分析到AI辅助诊断
  • ARM虚拟化中ICH_HFGWTR_EL2寄存器解析与应用
  • 嵌入式开发:从8/16位到32位ARM处理器的迁移指南
  • 杰理之设置IO状态的方法【篇】
  • PyTorch 自定义算子开发:C++ 扩展与 CUDA 加速
  • AGI与大型模型如何重塑医学影像分析:从专用工具到通用智能体
  • Unity C#入门:类与对象的基础认知与创建
  • 为AI代理注入情感氛围:agent-vibes项目设计与实战解析
  • 本地部署大语言模型实战:基于gpt4local的私有化AI解决方案
  • 杰理之添加AD通道的接口【篇】
  • 高效向量化Trie:加速器上的LLM生成检索约束解码技术
  • PyTorch 模型并行策略:数据并行 vs 模型并行
  • FPGA低功耗设计原理与工程实践优化
  • AI与XR技术融合:构建心脏健康数字孪生,重塑精准医疗
  • AI安全前沿:AI大模型安全防护的前沿技术
  • AItrika:基于LLM与RAG的医学文献智能解析工具实战指南