Springboot3 + Springboot cache+Ehcache3 + Redisson 实现本地缓存管理及分布式本地缓存更新方案
'# Springboot3 + Springboot cache+Ehcache3 + Redisson 实现本地缓存管理及分布式本地缓存更新方案
一、背景与问题
在分布式系统中,缓存是提升性能的重要手段。传统做法常使用Redis这样的分布式缓存,但存在内存占用高、网络延迟和数据一致性等挑战。而本地缓存(Local Cache)作为缓存的轻量化方案,能显著降低网络开销,但面临多实例数据同步、缓存失效和并发更新等难题。
在Spring Boot 3生态中,我们需要一个既能利用本地缓存的高性能优势,又能解决分布式场景下缓存一致性问题的方案。本方案结合Spring Cache、Ehcache3和Redisson,构建了一个分布式本地缓存更新系统,通过本地缓存+分布式消息队列的双层架构,在保证高性能的同时实现多实例数据同步。
二、基本原理
1. 层级缓存架构
graph TD
A[用户请求] --> B[本地缓存]
B --> C{命中?}
C -->|是| D[返回缓存数据]
C -->|否| E[调用数据库]
E --> F[更新本地缓存]
F --> G[推送更新消息]
G --> H[其他节点接收消息]
H --> I[更新本地缓存]2. 技术栈协同原理
- Spring Cache:提供缓存注解和抽象层
- Ehcache3:本地缓存的高性能实现
- Redisson:分布式锁和消息队列的协调工具
3. 关键技术点
- 缓存失效策略:基于TTL的过期机制 + 热点数据的永不过期策略
- 分布式更新:通过Redisson的发布订阅机制实现缓存同步
- 并发控制:使用Redisson锁保证更新操作的原子性
三、环境准备
1. 依赖配置(Spring Boot 3.1.5)
<dependencies>
<!-- Spring Boot Cache -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-cache</artifactId>
</dependency>
<!-- Ehcache3 -->
<dependency>
<groupId>org.ehcache</groupId>
<artifactId>ehcache</artifactId>
<version>3.12.3</version>
</dependency>
<!-- Redisson -->
<dependency>
<groupId>org.redisson</groupId>
<artifactId>redisson-spring-boot-starter</artifactId>
<version>3.18.7</version>
</dependency>
</dependencies>2. 配置文件(application.yml)
spring:
cache:
type: ehcache
ehcache:
config: class-path:ehcache.xml<!-- ehcache.xml -->
<ehcache xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:noNamespaceSchemaLocation="http://www.ehcache.org/ehcache3.0.xsd">
<cache name="userCache" maxEntriesLocalHeap="1000" eternal="false" timeToLiveSeconds="3600"/>
</ehcache>四、核心实现
1. 缓存注解配置
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
EhcacheCacheManager cacheManager = (EhcacheCacheManager) CacheManager.create();
return cacheManager;
}
}2. 缓存注解使用示例
@Service
public class UserService {
@Cacheable(value = "userCache", key = "#userId")
public User getUserById(Long userId) {
// 模拟数据库查询
return new User(userId, "Alice");
}
@CachePut(value = "userCache", key = "#user.id")
public User updateUser(User user) {
// 模拟更新数据库
return user;
}
}3. Redisson分布式更新实现
@Component
public class CacheUpdateService {
@Autowired
private RedissonClient redissonClient;
public void updateCache(Long userId, User user) {
RTopic topic = redissonClient.getTopic("userCacheUpdate");
topic.publish(new CacheUpdateEvent(userId, user));
}
@Data
public static class CacheUpdateEvent {
private Long userId;
private User user;
public CacheUpdateEvent(Long userId, User user) {
this.userId = userId;
this.user = user;
}
}
}@Component
public class CacheListener {
@Autowired
private CacheManager cacheManager;
@Autowired
private RedissonClient redissonClient;
public CacheListener() {
RTopic topic = redissonClient.getTopic("userCacheUpdate");
topic.addListener(CacheUpdateEvent.class, (channel, event) -> {
Cache<Object, Object> userCache = cacheManager.getCache("userCache");
userCache.put(event.getUserId(), event.getUser());
});
}
}五、完整案例
1. 示例项目结构
src
└── main
└── java
└── com.example
├── config
│ └── CacheConfig.java
├── service
│ ├── UserService.java
│ └── CacheUpdateService.java
├── listener
│ └── CacheListener.java
└── CacheApplication.java2. 完整案例代码
// UserService.java
@Service
public class UserService {
@Cacheable(value = "userCache", key = "#userId")
public User getUserById(Long userId) {
// 模拟数据库查询
return new User(userId, "Alice");
}
@CachePut(value = "userCache", key = "#user.id")
public User updateUser(User user) {
// 模拟更新数据库
return user;
}
}// CacheUpdateService.java
@Component
public class CacheUpdateService {
@Autowired
private RedissonClient redissonClient;
public void updateCache(Long userId, User user) {
RTopic topic = redissonClient.getTopic("userCacheUpdate");
topic.publish(new CacheUpdateEvent(userId, user));
}
@Data
public static class CacheUpdateEvent {
private Long userId;
private User user;
public CacheUpdateEvent(Long userId, User user) {
this.userId = userId;
this.user = user;
}
}
}// CacheListener.java
@Component
public class CacheListener {
@Autowired
private CacheManager cacheManager;
@Autowired
private RedissonClient redissonClient;
public CacheListener() {
RTopic topic = redissonClient.getTopic("userCacheUpdate");
topic.addListener(CacheUpdateEvent.class, (channel, event) -> {
Cache<Object, Object> userCache = cacheManager.getCache("userCache");
userCache.put(event.getUserId(), event.getUser());
});
}
}六、源码解析
1. Ehcache3 缓存机制
Ehcache3 使用两级缓存策略:
- 本地内存缓存:每个应用实例的内存空间
- 分布式缓存:通过Redisson实现的共享缓存
核心代码:
public class EhcacheCacheManager implements CacheManager {
private final CacheManager ehcacheManager;
public EhcacheCacheManager() {
this.ehcacheManager = CacheManager.create();
}
public Cache<Object, Object> getCache(String name) {
return ehcacheManager.getCache(name);
}
}2. Redisson 消息队列机制
Redisson 使用发布订阅模式实现分布式通信:
RTopic topic = redissonClient.getTopic("userCacheUpdate");
topic.publish(new CacheUpdateEvent(userId, user));源码关键点:
- 使用Redis的Pub/Sub功能
- 通过RedissonClient建立连接
- 消息队列的异步处理机制
七、进阶使用
1. 缓存更新策略优化
// 设置缓存更新策略
CacheConfiguration config = CacheConfiguration
.newCacheConfigurationBuilder(
Long.class, User.class,
ResourcePoolsBuilder.heap(1000)
)
.withExpiry(Expiry.timeToLiveSeconds(3600))
.build();2. 分布式锁控制
public void updateCacheSafely(Long userId, User user) {
RLock lock = redissonClient.getLock("userCacheLock-" + userId);
try {
if (lock.tryLock(3, 10, TimeUnit.SECONDS)) {
// 执行更新逻辑
}
} finally {
lock.unlock();
}
}3. 缓存热数据持久化
@Cacheable(value = "userCache", key = "#userId", unless = "#result == null")
public User getHotUser(Long userId) {
// 模拟热数据查询
return new User(userId, "HotUser");
}八、性能与工程实践
1. 性能优化策略
- 缓存命中率:通过
@Cacheable的unless条件控制缓存更新 - 内存管理:配置
maxEntriesLocalHeap限制内存占用 - 异步更新:使用Redisson的异步API减少阻塞
2. 异常处理方案
@Cacheable(value = "userCache", key = "#userId", unless = "#result == null")
public User getSafeUser(Long userId) {
try {
return getUserFromDatabase(userId);
} catch (Exception e) {
log.error("获取用户缓存失败", e);
return null;
}
}3. 安全风险防控
- 敏感数据加密:使用
AES加密缓存内容 - 缓存雪崩防护:设置随机TTL
- 缓存穿透防护:使用布隆过滤器
九、常见问题与踩坑
1. 常见错误
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 缓存未命中 | 缓存配置错误 | 检查ehcache.xml配置 |
| 更新不一致 | Redisson连接异常 | 检查Redis服务状态 |
| 内存溢出 | 本地缓存过大 | 调整maxEntriesLocalHeap |
2. 常见坑点
- 缓存更新顺序问题:在分布式系统中,需要保证更新消息的顺序性
- 并发更新冲突:未使用锁机制导致数据不一致
- 缓存失效策略错误:未合理设置TTL导致缓存命中率下降
十、最佳实践
1. 推荐实践
- 混合缓存策略:热数据使用本地缓存,冷数据使用分布式缓存
- 渐进式更新:使用Redisson的异步更新机制
- 监控告警:集成Prometheus监控缓存命中率和内存使用
2. 代码规范
- 命名规范:缓存名称使用
domain+entity格式(如userCache) - 注解规范:
@Cacheable使用key表达式避免重复 - 锁机制:使用Redisson锁控制更新操作
十一、总结
Spring Boot 3结合Ehcache3和Redisson实现的分布式本地缓存方案,通过本地缓存+分布式消息队列的架构,在保证高性能的同时解决了多实例数据同步的问题。这种方案适用于:
✅ 高并发场景:需要快速响应的业务系统
✅ 分布式系统:多个实例需要共享缓存数据
✅ 热数据处理:需要快速访问的高频数据
但需注意:
❌ 不适合:数据一致性要求极高的场景
❌ 不适合:需要持久化存储的场景
❌ 不适合:内存资源有限的轻量级系统
通过合理配置和实践,这种方案可以显著提升系统性能,同时保持良好的可维护性和扩展性。在实际开发中,建议结合监控系统持续优化缓存策略,以达到最佳性能平衡。
评论已关闭