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

Spring Boot Redis 缓存完全指南

Spring Boot Redis 缓存完全指南

1. 项目依赖配置

1.1 Maven依赖

<dependencies><!-- Spring Boot Redis --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Spring Boot Cache --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!-- Apache Commons Pool2 - Redis连接池需要 --><dependency><groupId>org.apache.commons</groupId><artifactId>commons-pool2</artifactId></dependency><!-- FastJson - 用于Redis序列化 --><dependency><groupId>com.alibaba</groupId><artifactId>fastjson</artifactId><version>1.2.83</version></dependency><!-- Lombok - 简化代码 --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><optional>true</optional></dependency>
</dependencies>

1.2 环境配置文件

application.yml 不同环境配置示例:

spring:# 开发环境配置profiles: devredis:host: localhostport: 6379password: database: 0timeout: 10000mslettuce:pool:max-active: 8max-wait: -1msmax-idle: 8min-idle: 0shutdown-timeout: 100ms---
# 生产环境配置
spring:profiles: prodredis:host: redis.prod.company.comport: 6379password: ${REDIS_PASSWORD}  # 使用环境变量database: 0timeout: 10000mslettuce:pool:max-active: 32max-wait: 3000msmax-idle: 16min-idle: 8shutdown-timeout: 100ms

2. Redis配置类详解

2.1 基础配置类

@Configuration
@EnableCaching
@Slf4j
public class RedisConfiguration {/*** Redis连接工厂配置*/@Beanpublic RedisConnectionFactory redisConnectionFactory(RedisProperties redisProperties) {LettuceConnectionFactory factory = new LettuceConnectionFactory();factory.setHostName(redisProperties.getHost());factory.setPort(redisProperties.getPort());factory.setPassword(redisProperties.getPassword());factory.setDatabase(redisProperties.getDatabase());return factory;}/*** FastJson序列化器配置*/@Beanpublic FastJsonRedisSerializer<Object> fastJsonRedisSerializer() {FastJsonRedisSerializer<Object> serializer = new FastJsonRedisSerializer<>(Object.class);FastJsonConfig config = new FastJsonConfig();// 序列化规则配置config.setSerializerFeatures(SerializerFeature.WriteClassName,SerializerFeature.WriteMapNullValue,SerializerFeature.PrettyFormat,SerializerFeature.WriteNullListAsEmpty,SerializerFeature.WriteNullStringAsEmpty);serializer.setFastJsonConfig(config);return serializer;}
}

2.2 缓存管理器配置

@Configuration
public class CacheManagerConfig {@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory connectionFactory, FastJsonRedisSerializer<Object> fastJsonRedisSerializer) {// 默认配置RedisCacheConfiguration defaultConfig = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30))  // 默认过期时间.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).disableCachingNullValues();// 特定缓存空间配置Map<String, RedisCacheConfiguration> configMap = new HashMap<>();// 短期缓存配置 - 适用于频繁更新的数据configMap.put("shortCache", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).disableCachingNullValues());// 长期缓存配置 - 适用于不经常更新的数据configMap.put("longCache", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(12)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).disableCachingNullValues());// 永久缓存配置 - 适用于基础数据configMap.put("permanentCache", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ZERO)  // 永不过期.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(RedisSerializer.string())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)).disableCachingNullValues());return RedisCacheManager.builder(connectionFactory).cacheDefaults(defaultConfig).withInitialCacheConfigurations(configMap).build();}
}

3. 缓存注解详细使用指南

3.1 @Cacheable 详解

public class CacheableExample {/*** 基本使用* value: 缓存空间名称* key: 缓存键,使用SpEL表达式* condition: 缓存条件* unless: 否定缓存条件*/@Cacheable(value = "userCache",key = "#id",condition = "#id != null",unless = "#result == null")public User getUser(Long id) {return userMapper.selectById(id);}/*** 复杂key示例* 组合多个参数作为key*/@Cacheable(value = "userCache",key = "#root.targetClass.simpleName + ':' + #name + ':' + #age")public List<User> getUsersByNameAndAge(String name, Integer age) {return userMapper.findByNameAndAge(name, age);}/*** 多缓存空间示例*/@Cacheable(cacheNames = {"shortCache", "backupCache"})public User getUserWithMultiCache(Long id) {return userMapper.selectById(id);}
}

3.2 @CachePut 详解

public class CachePutExample {/*** 更新缓存示例* 总是执行方法,并将结果更新到缓存*/@CachePut(value = "userCache",key = "#user.id",condition = "#user.age > 18")public User updateUser(User user) {userMapper.updateById(user);return user;}/*** 批量更新缓存示例*/@CachePut(value = "userCache",key = "#user.id + ':' + #user.version")public List<User> batchUpdateUsers(List<User> users) {userMapper.batchUpdate(users);return users;}
}

3.3 @CacheEvict 详解

public class CacheEvictExample {/*** 删除指定缓存*/@CacheEvict(value = "userCache",key = "#id")public void deleteUser(Long id) {userMapper.deleteById(id);}/*** 批量删除缓存* allEntries = true 表示清除所有缓存* beforeInvocation = true 表示在方法执行前清除缓存*/@CacheEvict(value = "userCache",allEntries = true,beforeInvocation = true)public void clearAllUserCache() {log.info("清除所有用户缓存");}/*** 多缓存空间清除*/@Caching(evict = {@CacheEvict(value = "userCache", key = "#user.id"),@CacheEvict(value = "roleCache", key = "#user.roleId"),@CacheEvict(value = "permissionCache", key = "#user.id")})public void deleteUserAndRelatedCache(User user) {userMapper.deleteById(user.getId());}
}

4. 实战场景示例

4.1 统计数据缓存

@Service
@Slf4j
public class StatisticsServiceImpl implements StatisticsService {/*** 政治面貌统计* 使用短期缓存,因为统计数据会定期更新*/@Cacheable(value = "statisticsCache",key = "'political_stats_' + #region",unless = "#result == null || #result.isEmpty()")public Map<String, Object> getPoliticalStats(String region) {log.info("计算{}地区政治面貌统计数据", region);return statisticsMapper.calculatePoliticalStats(region);}/*** 定时更新缓存*/@Scheduled(cron = "0 0 1 * * ?")  // 每天凌晨1点执行@CacheEvict(value = "statisticsCache", allEntries = true)public void refreshStatisticsCache() {log.info("刷新统计数据缓存");}
}

4.2 多级缓存示例

@Service
@Slf4j
public class UserServiceImpl implements UserService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 多级缓存实现* 1. 先查本地缓存* 2. 再查Redis缓存* 3. 最后查数据库*/@Cacheable(value = "userCache",key = "#id",unless = "#result == null")public User getUserWithMultiLevelCache(Long id) {// 1. 查询本地缓存(使用Caffeine实现)User user = localCache.getIfPresent(id);if (user != null) {log.debug("从本地缓存获取用户: {}", id);return user;}// 2. 查询Redis缓存String redisKey = "user:" + id;user = (User) redisTemplate.opsForValue().get(redisKey);if (user != null) {log.debug("从Redis缓存获取用户: {}", id);// 放入本地缓存localCache.put(id, user);return user;}// 3. 查询数据库user = userMapper.selectById(id);if (user != null) {log.debug("从数据库获取用户: {}", id);// 放入本地缓存localCache.put(id, user);// 放入Redis缓存redisTemplate.opsForValue().set(redisKey, user, 30, TimeUnit.MINUTES);}return user;}
}

4.3 分布式锁与缓存结合

@Service
@Slf4j
public class OrderServiceImpl implements OrderService {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 使用分布式锁防止缓存击穿*/@Cacheable(value = "orderCache",key = "#orderId",unless = "#result == null")public Order getOrderWithLock(String orderId) {String lockKey = "lock:order:" + orderId;boolean locked = false;try {// 尝试获取分布式锁locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "LOCKED", 10, TimeUnit.SECONDS);if (!locked) {// 未获取到锁,等待后重试Thread.sleep(100);return getOrderWithLock(orderId);}// 获取到锁,执行业务逻辑Order order = orderMapper.selectById(orderId);if (order != null) {// 添加缓存redisTemplate.opsForValue().set("order:" + orderId, order, 30, TimeUnit.MINUTES);}return order;} catch (InterruptedException e) {log.error("获取订单信息异常", e);Thread.currentThread().interrupt();return null;} finally {// 释放锁if (locked) {redisTemplate.delete(lockKey);}}}
}

5. 缓存监控与维护

5.1 缓存监控配置

@Configuration
public class CacheMonitorConfig {@Beanpublic CacheMetricsCollector cacheMetricsCollector(CacheManager cacheManager) {CacheMetricsCollector collector = new CacheMetricsCollector();// 注册缓存指标collector.bindCacheManager(cacheManager);return collector;}
}

5.2 自定义缓存监听器

@Component
public class CustomCacheEventListener extends CacheEventListenerAdapter {@Overridepublic void onEvent(CacheEvent event) {log.info("Cache event {} for key {} in cache {}", event.getType(), event.getKey(), event.getCache().getName());}
}

5.3 缓存统计

@Component
@Slf4j
public class CacheStats {@Autowiredprivate CacheManager cacheManager;/*** 收集缓存统计信息*/@Scheduled(fixedRate = 300000) // 每5分钟执行一次public void collectCacheStats() {Map<String, Map<String, Object>> stats = new HashMap<>();cacheManager.getCacheNames().forEach(cacheName -> {Cache cache = cacheManager.getCache(cacheName);if (cache instanceof RedisCache) {RedisCache redisCache = (RedisCache) cache;Map<String, Object> cacheStats = new HashMap<>();cacheStats.put("size", redisCache.estimatedSize());cacheStats.put("hitCount", redisCache.getStats().getHitCount());cacheStats.put("missCount", redisCache.getStats().getMissCount());stats.put(cacheName, cacheStats);}});log.info("Cache statistics: {}", stats);}
}

6. 最佳实践与注意事项

6.1 缓存键设计规范

public class CacheKeyConstants {// 使用常量定义缓存键前缀public static final String USER_CACHE_PREFIX = "user:";public static final String ORDER_CACHE_PREFIX = "order:";public static final String PRODUCT_CACHE_PREFIX = "product:";// 组合缓存键public static String getUserCacheKey(Long userId) {return USER_CACHE_PREFIX + userId;}public static String getOrderCacheKey(String orderId, String type) {return String.format("%s:%s:%s", ORDER_CACHE_PREFIX, type, orderId);}
}

6.2 缓存异常处理

@Aspect
@Component
@Slf4j
public class CacheErrorHandler {@Around("@annotation(org.springframework.cache.annotation.Cacheable)")public Object handleCacheError(ProceedingJoinPoint pjp) {try {return pjp.proceed();} catch (Throwable e) {log.error("Cache operation failed", e);// 降级处理:直接访问数据库try {return pjp.proceed();} catch (Throwable ex) {log.error("Database operation also failed", ex);throw new RuntimeException("Service unavailable", ex);}}}
}

6.3 缓存预热

@Component
public class CacheWarmer implements ApplicationRunner {@Autowiredprivate UserService userService;@Overridepublic void run(ApplicationArguments args) {log.info("开始预热缓存...");// 预热重要的用户数据List<Long> importantUserIds = userService.getImportantUserIds();importantUserIds.forEach(userId -> {try {userService.getUserById(userId);log.debug("预热用户缓存: {}", userId);} catch (Exception e) {log.error("预热用户缓存失败: {}", userId, e);}});log.info("缓存预热完成");}
}

7. 性能优化建议

  1. 合理设置缓存大小和过期时间
@Configuration
public class CacheConfig {@Beanpublic RedisCacheManager cacheManager(RedisConnectionFactory factory) {// 根据实际业务需求设置缓存配置Map<String, RedisCacheConfiguration> configMap = new HashMap<>();// 高频访问数据 - 短期缓存configMap.put("highFrequency", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).prefixCacheNameWith("hf:").serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)));// 低频访问数据 - 长期缓存configMap.put("lowFrequency", RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofHours(24)).prefixCacheNameWith("lf:").serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer)));return RedisCacheManager.builder(factory).withInitialCacheConfigurations(configMap).build();}
}
  1. 使用批量操作提高性能
@Service
public class BatchOperationExample {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 批量获取用户信息*/public List<User> batchGetUsers(List<Long> userIds) {List<String> keys = userIds.stream().map(id -> "user:" + id).collect(Collectors.toList());// 批量获取缓存List<Object> cachedUsers = redisTemplate.opsForValue().multiGet(keys);// 处理缓存未命中的情况List<Long> missedIds = new ArrayList<>();List<User> result = new ArrayList<>();for (int i = 0; i < userIds.size(); i++) {if (cachedUsers.get(i) != null) {result.add((User) cachedUsers.get(i));} else {missedIds.add(userIds.get(i));}}if (!missedIds.isEmpty()) {// 批量查询数据库List<User> dbUsers = userMapper.batchSelect(missedIds);// 批量写入缓存Map<String, User> userMap = new HashMap<>();dbUsers.forEach(user -> userMap.put("user:" + user.getId(), user));redisTemplate.opsForValue().multiSet(userMap);result.addAll(dbUsers);}return result;}
}
  1. 使用管道提高性能
@Service
public class PipelineExample {@Autowiredprivate RedisTemplate<String, Object> redisTemplate;/*** 使用管道批量操作*/public void batchOperationWithPipeline(List<User> users) {redisTemplate.executePipelined((RedisCallback<Object>) connection -> {for (User user : users) {byte[] key = redisTemplate.getKeySerializer().serialize("user:" + user.getId());byte[] value = redisTemplate.getValueSerializer().serialize(user);connection.set(key, value);connection.expire(key, 1800); // 30分钟过期}return null;});}
}

8. 缓存安全

8.1 防止缓存穿透

@Service
public class CachePenetrationProtection {/*** 使用布隆过滤器防止缓存穿透*/@Cacheable(value = "userCache",key = "#id",unless = "#result == null")public User getUserWithBloomFilter(Long id) {// 先检查布隆过滤器if (!bloomFilter.mightContain(id)) {return null;}// 查询缓存User user = (User) redisTemplate.opsForValue().get("user:" + id);if (user != null) {return user;}// 查询数据库user = userMapper.selectById(id);if (user != null) {redisTemplate.opsForValue().set("user:" + id, user, 30, TimeUnit.MINUTES);} else {// 防止缓存穿透,缓存空值redisTemplate.opsForValue().set("user:" + id, NULL_VALUE, 5, TimeUnit.MINUTES);}return user;}
}

8.2 防止缓存击穿

@Service
public class CacheBreakdownProtection {/*** 使用互斥锁防止缓存击穿*/public User getUserWithMutex(Long id) {String cacheKey = "user:" + id;String lockKey = "lock:" + cacheKey;// 查询缓存User user = (User) redisTemplate.opsForValue().get(cacheKey);if (user != null) {return user;}// 获取互斥锁boolean locked = redisTemplate.opsForValue().setIfAbsent(lockKey, "1", 10, TimeUnit.SECONDS);if (!locked) {// 获取锁失败,等待后重试try {Thread.sleep(100);return getUserWithMutex(id);} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException("获取用户信息失败", e);}}try {// 双重检查user = (User) redisTemplate.opsForValue().get(cacheKey);if (user != null) {return user;}// 查询数据库user = userMapper.selectById(id);if (user != null) {redisTemplate.opsForValue().set(cacheKey, user, 30, TimeUnit.MINUTES);}return user;} finally {// 释放锁redisTemplate.delete(lockKey);}}
}

8.3 防止缓存雪崩

@Service
public class CacheAvalancheProtection {/*** 使用随机过期时间防止缓存雪崩*/@Cacheable(value = "userCache",key = "#id",unless = "#result == null")public User getUserWithRandomExpiry(Long id) {User user = userMapper.selectById(id);if (user != null) {// 基础过期时间30分钟,增加随机值(0-5分钟)long randomExpiry = 30 + new Random().nextInt(5);redisTemplate.opsForValue().set("user:" + id, user, randomExpiry, TimeUnit.MINUTES);}return user;}
}

9. 缓存测试

9.1 单元测试

@SpringBootTest
public class CacheTest {@Autowiredprivate UserService userService;@Autowiredprivate CacheManager cacheManager;@Testpublic void testUserCache() {// 第一次调用,应该查询数据库User user1 = userService.getUserById(1L);assertNotNull(user1);// 第二次调用,应该从缓存获取User user2 = userService.getUserById(1L);assertNotNull(user2);assertEquals(user1, user2);// 验证缓存中的数据Cache cache = cacheManager.getCache("userCache");assertNotNull(cache);assertNotNull(cache.get("user:1"));}
}

9.2 性能测试

@SpringBootTest
public class CachePerformanceTest {@Autowiredprivate UserService userService;@Testpublic void testCachePerformance() {int iterations = 1000;long startTime = System.currentTimeMillis();// 预热缓存User user = userService.getUserById(1L);assertNotNull(user);// 测试缓存读取性能for (int i = 0; i < iterations; i++) {user = userService.getUserById(1L);assertNotNull(user);}long endTime = System.currentTimeMillis();long duration = endTime - startTime;log.info("平均响应时间: {}ms", duration / (double) iterations);}
}

这个详细的教程涵盖了 Spring Boot Redis 缓存的各个方面,从基础配置到高级特性,以及性能优化和安全考虑。你可以根据具体项目需求选择合适的部分进行使用和调整。

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

相关文章:

  • ApiPost 设置统一URL前缀
  • 计算机基础速通--数据结构·串的应用
  • 医防融合中心-智慧化慢病全程管理医疗AI系统开发(中)
  • 元数据管理与数据治理平台:Apache Atlas 构建与安装 Building Installing Apache Atlas
  • 有哪些产品需要遵循ASTM D4169-23e1
  • 【ee类保研面试】其他类---计算机网络
  • 操作系统:多线程模型(Multithreading Models)与超线程技术(Hyperthreading)
  • AI漫画翻译器-上传图片自动翻译,支持多语言
  • 学习Java的Day27
  • 基于ffmpeg和rk3588的mpp编解码库多路融屏程序设计
  • Git 基础操作笔记(速查)
  • 嵌入式Linux学习 - 数据结构6
  • 【设计模式】抽象工厂模式 (工具(Kit)模式)
  • PPT科研绘图实践笔记(持续更新)
  • AI 编程工具使用心得与对比评测
  • Python实现点云PCA配准——粗配准
  • 三种经典寻路算法对比
  • 微服务的好与坏
  • kafak
  • 经常问的14000
  • HTML5 Web Workers 深度剖析:助力网页性能飞速提升
  • imx6ull-驱动开发篇14——原子操作
  • FFmpeg 视频旋转信息处理:3.4 vs 7.0.2
  • 开发避坑指南(22):Vue3响应式编程中this绑定机制与解决方案
  • C++ 部署LSTM(.onnx)
  • 大模型中的核心参数temperature 您知道是什么东东吗?
  • KEIL 环境下 printf 导致程序无法执行的解决方案
  • GPT5评测对比与使用
  • 2025年城市建设与智慧交通国际会议(ICUCIT 2025)
  • OpenAI重磅开源回归!GPT-OSS-120B/20B登陆星辰MaaS