【实战】Spring Boot 3.x整合Redis:注解式缓存与分布式锁最佳实践
Spring Boot 3.x整合Redis:注解式缓存与分布式锁最佳实践
- 一、环境准备与基础配置
- 1.1 项目初始化
- 1.2 Redis配置类
- 二、注解式缓存实战
- 2.1 缓存核心注解
- 2.2 服务层缓存实现
- 2.3 自定义Key生成器
- 三、分布式锁实现
- 3.1 Redisson分布式锁
- 3.2 注解式分布式锁
- 四、缓存与锁的流程图解
- 4.1 注解式缓存流程
- 4.2 分布式锁流程
- 五、高级特性与最佳实践
- 5.1 缓存雪崩/穿透/击穿防护
- 5.2 多级缓存策略
- 5.3 分布式锁最佳实践
- 六、性能优化与监控
- 6.1 Redis连接池配置
- 6.2 缓存命中率监控
- 6.3 Redisson监控指标
- 七、常见问题解决方案
- 7.1 缓存一致性解决方案
- 7.2 分布式锁重入问题
- 八、总结与最佳实践清单
- 8.1 缓存最佳实践
- 8.2 分布式锁最佳实践
- 8.3 监控指标清单
一、环境准备与基础配置
1.1 项目初始化
首先创建一个Spring Boot 3.x项目并添加必要依赖:
<!-- pom.xml -->
<dependencies><!-- Spring Boot Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- Redis Starter --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency><!-- Redis客户端(推荐Lettuce) --><dependency><groupId>io.lettuce</groupId><artifactId>lettuce-core</artifactId><version>6.2.3.RELEASE</version></dependency><!-- 分布式锁工具 --><dependency><groupId>org.redisson</groupId><artifactId>redisson-spring-boot-starter</artifactId><version>3.23.1</version></dependency>
</dependencies>
1.2 Redis配置类
@Configuration
@EnableCaching
public class RedisConfig {@Beanpublic RedisConnectionFactory redisConnectionFactory() {LettuceConnectionFactory factory = new LettuceConnectionFactory();factory.setHostName("localhost");factory.setPort(6379);factory.setPassword("yourpassword");factory.setDatabase(0);return factory;}@Beanpublic RedisTemplate<String, Object> redisTemplate() {RedisTemplate<String, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory());// 使用StringRedisSerializer序列化keytemplate.setKeySerializer(new StringRedisSerializer());// 使用GenericJackson2JsonRedisSerializer序列化valuetemplate.setValueSerializer(new GenericJackson2JsonRedisSerializer());template.setHashKeySerializer(new StringRedisSerializer());template.setHashValueSerializer(new GenericJackson2JsonRedisSerializer());template.afterPropertiesSet();return template;}@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)) // 默认缓存30分钟.disableCachingNullValues() // 不缓存null值.serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));return RedisCacheManager.builder(factory).cacheDefaults(config).transactionAware().build();}@Beanpublic RedissonClient redissonClient() {Config config = new Config();config.useSingleServer().setAddress("redis://localhost:6379").setPassword("yourpassword").setDatabase(0);return Redisson.create(config);}
}
二、注解式缓存实战
2.1 缓存核心注解
Spring Cache提供了四个核心注解:
注解 | 说明 | 常用属性 |
---|---|---|
@Cacheable | 方法结果缓存 | value/cacheNames、key、condition、unless |
@CacheEvict | 清除缓存 | value/cacheNames、key、allEntries、beforeInvocation |
@CachePut | 更新缓存 | 同@Cacheable |
@Caching | 组合多个缓存操作 | cacheable、put、evict |
2.2 服务层缓存实现
@Service
public class ProductServiceImpl implements ProductService {private final ProductRepository productRepository;@Autowiredpublic ProductServiceImpl(ProductRepository productRepository) {this.productRepository = productRepository;}@Cacheable(value = "products", key = "#id", unless = "#result == null")@Overridepublic Product getProductById(Long id) {return productRepository.findById(id).orElse(null);}@CachePut(value = "products", key = "#product.id")@Overridepublic Product updateProduct(Product product) {return productRepository.save(product);}@CacheEvict(value = "products", key = "#id")@Overridepublic void deleteProduct(Long id) {productRepository.deleteById(id);}@Caching(evict = {@CacheEvict(value = "productList", allEntries = true),@CacheEvict(value = "products", key = "#product.id")})@Overridepublic Product saveProduct(Product product) {return productRepository.save(product);}
}
2.3 自定义Key生成器
@Configuration
public class CacheKeyGeneratorConfig {@Bean("customKeyGenerator")public KeyGenerator keyGenerator() {return (target, method, params) -> {StringBuilder sb = new StringBuilder();sb.append(target.getClass().getSimpleName());sb.append(":");sb.append(method.getName());sb.append(":");return Arrays.stream(params).map(param -> param != null ? param.toString() : "").collect(Collectors.joining("_"));};}
}// 使用示例
@Cacheable(value = "products", keyGenerator = "customKeyGenerator")
public Product getProductByCode(String code) {// ...
}
三、分布式锁实现
3.1 Redisson分布式锁
@Service
public class OrderServiceImpl implements OrderService {private final RedissonClient redissonClient;private final OrderRepository orderRepository;@Autowiredpublic OrderServiceImpl(RedissonClient redissonClient, OrderRepository orderRepository) {this.redissonClient = redissonClient;this.orderRepository = orderRepository;}public boolean createOrder(Order order) {String lockKey = "order_lock:" + order.getUserId();RLock lock = redissonClient.getLock(lockKey);try {// 尝试获取锁,最多等待5秒,锁自动释放时间30秒boolean locked = lock.tryLock(5, 30, TimeUnit.SECONDS);if (!locked) {throw new RuntimeException("获取锁失败");}// 检查库存if (checkInventory(order)) {// 扣减库存reduceInventory(order);// 创建订单orderRepository.save(order);return true;}return false;} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException("锁等待被中断", e);} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}}}// 其他方法...
}
3.2 注解式分布式锁
创建自定义注解简化锁的使用:
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface DistributedLock {String key(); // 锁的keylong waitTime() default 5; // 获取锁等待时间(秒)long leaseTime() default 30; // 锁持有时间(秒)TimeUnit timeUnit() default TimeUnit.SECONDS;
}@Aspect
@Component
@RequiredArgsConstructor
public class DistributedLockAspect {private final RedissonClient redissonClient;@Around("@annotation(distributedLock)")public Object around(ProceedingJoinPoint joinPoint, DistributedLock distributedLock) throws Throwable {String lockKey = distributedLock.key();RLock lock = redissonClient.getLock(lockKey);try {boolean locked = lock.tryLock(distributedLock.waitTime(),distributedLock.leaseTime(),distributedLock.timeUnit());if (!locked) {throw new RuntimeException("获取分布式锁失败");}return joinPoint.proceed();} catch (InterruptedException e) {Thread.currentThread().interrupt();throw new RuntimeException("锁等待被中断", e);} finally {if (lock.isLocked() && lock.isHeldByCurrentThread()) {lock.unlock();}}}
}// 使用示例
@Service
public class PaymentService {@DistributedLock(key = "'payment:' + #payment.userId", leaseTime = 60)public boolean processPayment(Payment payment) {// 支付处理逻辑}
}
四、缓存与锁的流程图解
4.1 注解式缓存流程
4.2 分布式锁流程
五、高级特性与最佳实践
5.1 缓存雪崩/穿透/击穿防护
@Configuration
public class CacheConfig {@Beanpublic CacheManager cacheManager(RedisConnectionFactory factory) {RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(30)).disableCachingNullValues().serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer())).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()))// 缓存穿透防护:空值缓存短时间.withEntryTtlForNullValues(Duration.ofSeconds(30))// 缓存雪崩防护:随机TTL.computePrefixWith(cacheName -> cacheName + ":" + ThreadLocalRandom.current().nextInt(10));return RedisCacheManager.builder(factory).cacheDefaults(config)// 缓存击穿防护:加锁加载.withInitialCacheConfigurations(Collections.singletonMap("hotProducts",RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofMinutes(5)).disableCachingNullValues())).transactionAware().build();}
}// 热点数据特殊处理
@Service
public class ProductServiceImpl implements ProductService {@Cacheable(value = "hotProducts", key = "#id", cacheManager = "cacheManager")public Product getHotProduct(Long id) {// ...}
}
5.2 多级缓存策略
@Service
public class ProductServiceImpl implements ProductService {@Cacheable(cacheNames = {"localCache", "redisCache"}, key = "#id")public Product getProductWithMultiCache(Long id) {return productRepository.findById(id).orElse(null);}@CacheEvict(cacheNames = {"localCache", "redisCache"}, key = "#id")public void refreshProduct(Long id) {// ...}
}// 本地Caffeine缓存配置
@Configuration
public class MultiLevelCacheConfig {@Beanpublic CacheManager cacheManager() {CaffeineCacheManager caffeineCacheManager = new CaffeineCacheManager();caffeineCacheManager.setCaffeine(Caffeine.newBuilder().expireAfterWrite(10, TimeUnit.MINUTES).maximumSize(1000));RedisCacheManager redisCacheManager = RedisCacheManager.create(redisConnectionFactory());return new CompositeCacheManager(caffeineCacheManager, redisCacheManager);}
}
5.3 分布式锁最佳实践
- 锁命名规范:
- 业务前缀: 业务:子业务:资源ID 如 order:create:123
- 全局锁: global🔒操作名 如 global🔒system_init
- 锁超时设置:
- 必须设置合理的leaseTime,防止死锁
- 建议比业务最大执行时间稍长
- 锁续期机制:
public class LockRenewal {private final RedissonClient redissonClient;private ScheduledExecutorService executorService;public LockRenewal(RedissonClient redissonClient) {this.redissonClient = redissonClient;this.executorService = Executors.newScheduledThreadPool(1);}public void renewLock(String lockKey, long leaseTime) {RLock lock = redissonClient.getLock(lockKey);if (lock.isHeldByCurrentThread()) {executorService.scheduleAtFixedRate(() -> {if (lock.isHeldByCurrentThread()) {lock.expire(leaseTime, TimeUnit.SECONDS);}}, leaseTime / 3, leaseTime / 3, TimeUnit.SECONDS);}}public void shutdown() {executorService.shutdown();}
}// 使用示例
@DistributedLock(key = "'long_task:' + #taskId", leaseTime = 300)
public void executeLongTask(String taskId) {LockRenewal renewal = new LockRenewal(redissonClient);try {renewal.renewLock("long_task:" + taskId, 300);// 执行长时间任务...} finally {renewal.shutdown();}
}
六、性能优化与监控
6.1 Redis连接池配置
# application.yml
spring:redis:lettuce:pool:max-active: 50max-idle: 20min-idle: 5max-wait: 5000timeout: 3000
6.2 缓存命中率监控
@Aspect
@Component
public class CacheMonitorAspect {private final MeterRegistry meterRegistry;@Autowiredpublic CacheMonitorAspect(MeterRegistry meterRegistry) {this.meterRegistry = meterRegistry;}@Around("@annotation(org.springframework.cache.annotation.Cacheable)")public Object monitorCacheable(ProceedingJoinPoint joinPoint) throws Throwable {String cacheName = getCacheName(joinPoint);Counter hits = meterRegistry.counter("cache.hits", "name", cacheName);Counter misses = meterRegistry.counter("cache.misses", "name", cacheName);try {Object result = joinPoint.proceed();hits.increment();return result;} catch (CacheableException e) {misses.increment();throw e;}}private String getCacheName(ProceedingJoinPoint joinPoint) {MethodSignature signature = (MethodSignature) joinPoint.getSignature();Cacheable cacheable = signature.getMethod().getAnnotation(Cacheable.class);return cacheable.value()[0];}
}
6.3 Redisson监控指标
@Configuration
public class RedissonMetricsConfig {@Autowiredpublic void registerRedissonMetrics(RedissonClient redissonClient, MeterRegistry meterRegistry) {meterRegistry.gauge("redisson.locks", redissonClient, client -> {return client.getKeys().countExists(client.getKeys().getKeysByPattern("*lock*"));});}
}
七、常见问题解决方案
7.1 缓存一致性解决方案
@Service
public class ProductServiceImpl implements ProductService {private final ProductRepository productRepository;private final CacheManager cacheManager;@Autowiredpublic ProductServiceImpl(ProductRepository productRepository,CacheManager cacheManager) {this.productRepository = productRepository;this.cacheManager = cacheManager;}@Transactional@CacheEvict(value = "products", key = "#product.id")public Product updateProduct(Product product) {Product updated = productRepository.save(product);// 双删策略evictCache(product.getId());try {Thread.sleep(500); // 等待500ms让消息队列传播} catch (InterruptedException e) {Thread.currentThread().interrupt();}evictCache(product.getId());return updated;}private void evictCache(Long productId) {Cache cache = cacheManager.getCache("products");if (cache != null) {cache.evict(productId);}}
}
7.2 分布式锁重入问题
@Service
public class OrderServiceImpl implements OrderService {private final RedissonClient redissonClient;@Autowiredpublic OrderServiceImpl(RedissonClient redissonClient) {this.redissonClient = redissonClient;}@DistributedLock(key = "'order:' + #orderId")public void processOrder(Long orderId) {// 处理订单updateOrderStatus(orderId);}@DistributedLock(key = "'order:' + #orderId")public void updateOrderStatus(Long orderId) {// 更新订单状态// 由于Redisson支持可重入锁,这里不会死锁}
}
八、总结与最佳实践清单
8.1 缓存最佳实践
- 合理设置TTL:根据业务特点设置不同的过期时间
- 避免大Value:单个缓存Value不超过1MB
- 缓存穿透防护:
- 空值缓存
- 布隆过滤器
- 缓存雪崩防护:
- 随机过期时间
- 多级缓存
- 缓存击穿防护:
- 互斥锁重建
- 永不过期+后台更新
8.2 分布式锁最佳实践
- 锁命名规范:业务:子业务:资源ID
- 设置合理超时:必须设置leaseTime
- 锁续期机制:长时间任务需自动续期
- 避免死锁:
- 获取锁后检查业务条件
- finally中确保释放锁
- 可重入锁:Redisson默认支持
8.3 监控指标清单
指标名称 | 类型 | 说明 |
---|---|---|
cache.hits | Counter | 缓存命中次数 |
cache.misses | Counter | 缓存未命中次数 |
redis.command.latency | Timer | Redis命令延迟 |
redisson.locks | Gauge | 当前持有的分布式锁数量 |
redis.connection.active | Gauge | 活跃Redis连接数 |
通过本指南,您应该能够在Spring Boot 3.x项目中高效地整合Redis,实现注解式缓存和分布式锁的最佳实践。记住根据实际业务需求调整配置,并持续监控系统性能。