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

【实战】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 注解式缓存流程

客户端ControllerServiceRedis缓存RepositoryHTTP请求调用方法检查缓存(key)返回缓存数据返回结果查询数据库返回数据写入缓存返回结果alt[缓存命中][缓存未命中]HTTP响应客户端ControllerServiceRedis缓存Repository

4.2 分布式锁流程

客户端1客户端2服务Redis请求资源A尝试获取锁A(lockA)获取成功执行业务逻辑请求资源A尝试获取锁A(lockA)获取失败(等待)par[并行请求]释放锁A释放成功返回结果通知Client2获取锁执行业务逻辑释放锁A返回结果客户端1客户端2服务Redis

五、高级特性与最佳实践

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 分布式锁最佳实践

  1. 锁命名规范:
    • 业务前缀: 业务:子业务:资源ID 如 order:create:123
    • 全局锁: global🔒操作名 如 global🔒system_init
  2. 锁超时设置:
    • 必须设置合理的leaseTime,防止死锁
    • 建议比业务最大执行时间稍长
  3. 锁续期机制:
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 缓存最佳实践

  1. 合理设置TTL:根据业务特点设置不同的过期时间
  2. 避免大Value:单个缓存Value不超过1MB
  3. 缓存穿透防护:
    • 空值缓存
    • 布隆过滤器
  4. 缓存雪崩防护:
    • 随机过期时间
    • 多级缓存
  5. 缓存击穿防护:
    • 互斥锁重建
    • 永不过期+后台更新

8.2 分布式锁最佳实践

  1. 锁命名规范:业务:子业务:资源ID
  2. 设置合理超时:必须设置leaseTime
  3. 锁续期机制:长时间任务需自动续期
  4. 避免死锁:
    • 获取锁后检查业务条件
    • finally中确保释放锁
  5. 可重入锁:Redisson默认支持

8.3 监控指标清单

指标名称类型说明
cache.hitsCounter缓存命中次数
cache.missesCounter缓存未命中次数
redis.command.latencyTimerRedis命令延迟
redisson.locksGauge当前持有的分布式锁数量
redis.connection.activeGauge活跃Redis连接数

通过本指南,您应该能够在Spring Boot 3.x项目中高效地整合Redis,实现注解式缓存和分布式锁的最佳实践。记住根据实际业务需求调整配置,并持续监控系统性能。

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

相关文章:

  • 密钥耳语-一个轻量、易备份、支持命令行与图形界面的口令派生加密工具 具有 CLI 和 GUI 的轻量级密码衍生加密工具
  • AI重塑流量背后,微软广告打造下一代广告生态
  • 低代码数字化时代的开发新范式
  • 微信小程序“无损去水印精灵”技术解析大纲
  • 少儿舞蹈小程序(18)订单确认
  • Uniapp X 打包抖音小程序教程
  • uni-app中实现在input的placeholder中添加图标
  • vue面试题集锦
  • 基于Springboot+UniApp+Ai实现模拟面试小工具十二:模拟面试功能实现
  • 基于Springboot+UniApp+Ai实现模拟面试小工具十一:主页功能及简历上传功能实现
  • BGP选路“十一步”法则
  • MITRE ATLAS 对抗威胁矩阵与 LLM 安全
  • 第5章:技术深度与广度:构筑你的核心壁垒(1)
  • 洞察未来:Temporal.io 如何赋能复杂模拟引擎的韧性与智能
  • Android 实例 - Android 圆形蒙版(Android 圆形蒙版实现、圆形蒙版解读)
  • PyCharm 在 Linux 上的安装指南
  • Linux 入门:开启开源世界的大门
  • ​​[硬件电路-321]:数字电路的两大家族CMOS(来源于MOS三极管管)与TTL(来源于BJT三极管)
  • 【GitHub每日速递 250922】开源 AI 搜索引擎 Perplexica:本地大模型 + 多模式搜索,免费又强大!
  • CCF-CSP-S 2021 初赛解析
  • 现在如何使用docker下载
  • 【Proteus仿真】AT89C51单片机并行数据转换为串行仿真
  • Docker 容器详解及实操,从新手>入门>拿捏,巨详细
  • 【ASP.NET Core】身份认证——Identity标识框架指南
  • [Nodejs+LangChain+Ollama] 2.提示词实践
  • i.MX6ULL移植内核6.6(二)GPIO子系统和LED子系统
  • VLC Media取消视频文件名前置显示
  • 在unity urp项目中 通过图片创建材质(透明光晕)
  • OneSignal PHP SDK v2 官方资源
  • 如何透过批次模拟加速3D模型建立