2. 缓存基础:本地缓存与分布式缓存的区别、常见缓存中间件(Redis、Caffeine)选型
在微服务架构中,缓存是提升系统吞吐量与降低响应延迟的核心手段。然而,Development环境(开发环境)与生产环境在流量规模、数据一致性要求、运维复杂度上存在显著差异。本章将系统梳理本地缓存与分布式缓存的本质区别,并针对 Redis 与 Caffeine 两款主流中间件给出选型建议,为后续搭建缓存一致性闭环奠定基础。
2.1 本地缓存 vs 分布式缓存:核心差异
缓存按存储位置与共享范围可分为本地缓存与分布式缓存。下表从多个维度对比两者的关键差异:
| 对比维度 | 本地缓存(Local Cache) | 分布式缓存(Distributed Cache) |
|---|---|---|
| 存储位置 | 进程内堆内存(JVM Heap) | 独立缓存服务(如 Redis 集群) |
| 数据共享 | 仅限单进程内可见 | 多进程/多节点全局共享 |
| 访问延迟 | 纳秒级(0.1~1 μs) | 毫秒级(0.5~5 ms,含网络开销) |
| 容量上限 | 受限于堆内存大小(通常 GB 级) | 可水平扩展至 TB 级 |
| 数据一致性 | 天然一致(单进程无并发冲突) | 需额外机制保证(如分布式锁、版本号) |
| 持久化 | 通常不持久化,进程重启即丢失 | 支持 RDB/AOF 等持久化策略 |
| 运维复杂度 | 零运维,随应用启动 | 需独立部署、监控、扩缩容 |
| 典型场景 | 热点数据、配置信息、无状态计算 | 会话共享、全局计数器、跨服务数据 |
Development 环境选型原则:开发阶段应优先使用本地缓存降低外部依赖,仅在需要验证分布式一致性或跨服务数据共享时引入分布式缓存。避免在开发环境过度依赖 Redis 集群,以减少环境搭建与调试成本。
2.2 常见缓存中间件选型:Redis vs Caffeine
在 Java 技术栈中,Caffeine 是本地缓存的标杆实现,Redis 则是分布式缓存的事实标准。以下从性能、功能、开发友好度三个维度进行对比:
| 选型维度 | Caffeine | Redis |
|---|---|---|
| 定位 | 进程内高性能缓存库 | 独立部署的键值存储系统 |
| 读写性能 | 极高(基于 ConcurrentHashMap + 淘汰算法优化) | 高(单线程事件循环,纯内存操作) |
| 淘汰策略 | W-TinyLFU(近最优命中率) | LRU、LFU、TTL、随机淘汰 |
| 数据结构 | 仅支持 Key-Value | String、Hash、List、Set、Sorted Set、Bitmap、HyperLogLog、Stream |
| 持久化 | 不支持 | RDB 快照 + AOF 日志 |
| 分布式能力 | 无 | 主从复制、哨兵、集群分片 |
| 开发集成 | Spring Cache 原生支持,零配置 | 需配置连接池、序列化、集群地址 |
| 适用阶段 | 开发、单元测试、单机部署 | 集成测试、预发、生产环境 |
选型建议:在 Development 环境中,推荐采用 Caffeine + Redis 双缓存策略——Caffeine 作为一级缓存(L1)处理高频热点读请求,Redis 作为二级缓存(L2)提供跨服务数据共享与一致性锚点。开发阶段可先仅启用 Caffeine,待需要验证分布式场景时再接入 Redis。
2.3 缓存架构选型决策流程
下图展示了在 Development 环境下,从需求分析到中间件选型的完整决策路径:
2.4 双缓存策略在 Development 环境中的落地
在开发阶段,推荐采用 Caffeine(L1)+ Redis(L2) 的双层缓存架构。其核心交互流程如下:
- 读请求:优先查询 Caffeine,命中则直接返回;未命中则查询 Redis,若 Redis 命中则回填 Caffeine 并返回;若均未命中则查询数据库,结果依次回填 Redis 和 Caffeine。
- 写请求:先更新数据库,再删除 Redis 中的对应缓存(延迟双删策略),最后清除本地 Caffeine 缓存,保证最终一致性。
- 缓存失效:Caffeine 设置较短 TTL(如 30~60 秒),Redis 设置较长 TTL(如 5~10 分钟),避免本地缓存长期不一致。
Development 环境避坑提示:
- 避免在开发环境使用 Redis 集群模式,单机模式足以验证功能,减少网络分区与故障演练的干扰。
- Caffeine 的最大容量不宜设置过大(建议 1000~5000 条),防止堆内存占用过高导致频繁 GC。
- 双缓存场景下务必实现缓存穿透保护(布隆过滤器或空值缓存),防止恶意 Key 打穿到数据库。
2.5 代码示例:Spring Boot 集成 Caffeine + Redis 双缓存
以下配置展示了在 Development 环境中快速启用双缓存的关键代码:
// 1. 引入依赖(pom.xml)
// Caffeine
implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8'
// Redis
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
// 2. 缓存配置类
@Configuration
@EnableCaching
public class CacheConfig {
@Bean
public CacheManager cacheManager() {
CaffeineCacheManager cacheManager = new CaffeineCacheManager();
cacheManager.setCaffeine(Caffeine.newBuilder()
.maximumSize(2000)
.expireAfterWrite(30, TimeUnit.SECONDS)
.recordStats());
return cacheManager;
}
@Bean
public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
RedisTemplate<String, Object> template = new RedisTemplate<>();
template.setConnectionFactory(factory);
template.setKeySerializer(new StringRedisSerializer());
template.setValueSerializer(new GenericJackson2JsonRedisSerializer());
return template;
}
}
// 3. 服务层使用示例
@Service
public class UserService {
@Autowired
private CacheManager cacheManager;
@Autowired
private RedisTemplate<String, Object> redisTemplate;
@Cacheable(value = "userCache", key = "#userId")
public User getUserById(Long userId) {
// 先查 Redis
String redisKey = "user:" + userId;
User user = (User) redisTemplate.opsForValue().get(redisKey);
if (user != null) {
return user;
}
// 查数据库
user = userMapper.selectById(userId);
if (user != null) {
redisTemplate.opsForValue().set(redisKey, user, 5, TimeUnit.MINUTES);
}
return user;
}
}
关键点说明:
@Cacheable注解默认使用 Caffeine 缓存,Redis 作为手动回填的二级缓存。- 开发阶段建议关闭 Redis 连接超时重试(设置
timeout=2s),避免 Redis 不可用时阻塞主流程。 - 使用
recordStats()可在开发时通过 Actuator 端点监控缓存命中率。
2.6 缓存选型总结矩阵
下表汇总了 Development 环境下不同场景的推荐选型:
| 场景特征 | 推荐方案 | 理由 |
|---|---|---|
| 单服务、无状态、高频读 | Caffeine 单层 | 零网络开销,开发调试简单 |
| 多服务共享会话/配置 | Redis 单层 | 全局一致性,无需本地同步 |
| 热点数据 + 跨服务一致性 | Caffeine + Redis 双层 | 兼顾性能与一致性,开发环境可先只启用 Caffeine |
| 验证分布式锁/计数器 | Redis 单层 | 利用 Redis 原子操作与 Lua 脚本 |
| 单元测试/快速原型 | Caffeine 单层 | 无需外部中间件,启动即用 |