4. 本地缓存与分布式缓存:Caffeine、Redis、EhCache 选型对比与适用边界
在微服务与分布式架构中,缓存一致性问题的根源往往在于“本地缓存”与“分布式缓存”的混合使用。选错缓存组件或混淆其适用边界,会导致数据不一致、内存溢出、网络瓶颈等连锁问题。本章从架构视角出发,对 Caffeine、Redis、EhCache 三款主流缓存进行深度对比,并给出清晰的适用边界。
4.1 缓存分类与核心差异
缓存按部署位置可分为两类:
- 本地缓存(进程内缓存):与应用运行在同一 JVM 进程中,如 Caffeine、EhCache、Guava Cache。访问速度极快(纳秒级),但受限于堆内存,且多实例间数据独立。
- 分布式缓存(远程缓存):独立部署的缓存服务,如 Redis、Memcached。数据集中存储,所有应用实例共享,但存在网络 I/O 开销(毫秒级)。
架构原则:本地缓存适合“读多写少、一致性要求低”的热点数据;分布式缓存适合“共享、需持久化、一致性要求高”的全局数据。两者常组合为“多级缓存”架构。
4.2 三款缓存组件核心对比
| 对比维度 | Caffeine | Redis | EhCache |
|---|---|---|---|
| 类型 | 本地缓存(进程内) | 分布式缓存(远程) | 本地缓存(进程内) |
| 数据存储 | 堆内存(Java 对象) | 内存 + 磁盘持久化 | 堆/堆外内存 + 磁盘 |
| 访问延迟 | 纳秒级(< 1μs) | 毫秒级(1-5ms) | 微秒级(1-10μs) |
| 淘汰策略 | W-TinyLFU(近最优) | LRU / LFU / TTL / 随机 | LRU / LFU / FIFO / TTL |
| 持久化 | 不支持 | RDB / AOF | 支持(磁盘存储) |
| 分布式支持 | 无(单机) | 原生集群 / 哨兵 / 分片 | Terracotta 集群(复杂) |
| 一致性模型 | 最终一致(单机强一致) | 最终一致(主从异步) | 最终一致(需集群协调) |
| Spring Boot 集成 | spring-boot-starter-cache + caffeine | spring-boot-starter-data-redis | spring-boot-starter-cache + ehcache |
4.3 选型决策树与适用边界
以下决策树可帮助团队快速确定缓存选型:
- 是否需要多实例共享数据?
- 是 → 走分布式缓存(Redis)
- 否 → 走本地缓存(Caffeine / EhCache)
- 本地缓存选哪个?
- 追求极致性能、高并发、低内存开销 → Caffeine
- 需要持久化、堆外内存、复杂配置 → EhCache
- 分布式缓存选哪个?
- 标准场景、数据结构丰富、持久化需求 → Redis
- 仅简单 KV、无持久化、极致简单 → Memcached(本章不展开)
推荐组合(Spring Boot 多级缓存):
Caffeine(L1 本地热点缓存)+ Redis(L2 分布式共享缓存)。
读操作:先查 Caffeine,未命中再查 Redis,回填 Caffeine。
写操作:更新 Redis,异步广播失效 Caffeine 中的对应 key。
Caffeine(L1 本地热点缓存)+ Redis(L2 分布式共享缓存)。
读操作:先查 Caffeine,未命中再查 Redis,回填 Caffeine。
写操作:更新 Redis,异步广播失效 Caffeine 中的对应 key。
4.4 架构示意图:多级缓存协作流程
4.5 代码示例:Spring Boot 多级缓存配置
以下配置展示了如何同时启用 Caffeine 和 Redis,并自定义 CacheManager 实现多级缓存策略:
@Configuration
@EnableCaching
public class MultiLevelCacheConfig {
// Caffeine 缓存管理器
@Bean
public CacheManager caffeineCacheManager() {
CaffeineCacheManager manager = new CaffeineCacheManager();
manager.setCaffeine(Caffeine.newBuilder()
.maximumSize(500)
.expireAfterWrite(60, TimeUnit.SECONDS)
.recordStats());
return manager;
}
// Redis 缓存管理器
@Bean
public CacheManager redisCacheManager(RedisConnectionFactory factory) {
RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig()
.entryTtl(Duration.ofMinutes(30))
.serializeKeysWith(
RedisSerializationContext.SerializationPair.fromSerializer(new StringRedisSerializer()))
.serializeValuesWith(
RedisSerializationContext.SerializationPair.fromSerializer(new GenericJackson2JsonRedisSerializer()));
return RedisCacheManager.builder(factory)
.cacheDefaults(config)
.build();
}
// 自定义多级缓存实现(简化示意)
@Bean
public CacheManager multiLevelCacheManager(
@Qualifier("caffeineCacheManager") CacheManager caffeine,
@Qualifier("redisCacheManager") CacheManager redis) {
return new CacheManager() {
@Override
public Cache getCache(String name) {
return new MultiLevelCache(caffeine.getCache(name), redis.getCache(name));
}
@Override
public Collection<String> getCacheNames() {
return caffeine.getCacheNames();
}
};
}
}
避坑提示:多级缓存中,本地缓存(Caffeine)的 TTL 应远小于 Redis 的 TTL(例如 60s vs 30min),避免本地缓存长期持有过期数据。同时,写操作必须保证 Redis 更新成功后再失效本地缓存,防止脏数据。
4.6 适用边界总结
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 单实例应用、高并发读 | Caffeine | 纳秒级访问,W-TinyLFU 淘汰策略命中率极高 |
| 多实例共享、数据需持久化 | Redis | 原生集群支持,RDB/AOF 持久化,数据结构丰富 |
| 需要堆外内存、磁盘存储 | EhCache | 支持堆外内存和磁盘溢出,适合大容量本地缓存 |
| 微服务多级缓存(标准架构) | Caffeine + Redis | 兼顾性能与一致性,业界主流实践 |
| 强一致性要求极高(如金融交易) | 仅用 Redis(或 DB) | 本地缓存无法保证多实例强一致,需避免使用 |
4.7 选型对比雷达图
4.8 本章小结
- Caffeine:本地缓存首选,性能极致,适合热点数据加速。
- Redis:分布式缓存标准,共享数据与持久化场景不可替代。
- EhCache:本地缓存备选,适合需要堆外内存或磁盘溢出的特殊场景。
- 多级缓存:Caffeine + Redis 组合是 Spring Boot 微服务架构的主流实践,需通过 TTL 和失效机制保证最终一致性。