缓存选型
缓存选型是系统架构设计的重要环节,选择合适的缓存方案可以显著提升系统性能。本文档详细介绍各种缓存类型的特点、适用场景和选型建议。
📋 缓存类型对比
1. 本地缓存
特点:
- 性能极高:内存访问,无网络开销
- 数据隔离:每个应用实例独立缓存
- 容量有限:受限于单机内存
- 数据不一致:多实例间数据不同步
适用场景:
- 单机应用
- 热点数据缓存
- 配置信息缓存
- 计算结果缓存
常见实现:
| 技术 | 特点 | 适用场景 |
|---|---|---|
| Caffeine | Java高性能缓存库 | Java应用本地缓存 |
| Guava Cache | Google缓存库 | 简单缓存需求 |
| Ehcache | Java缓存框架 | 企业级应用 |
| Cache2k | 高性能缓存 | 高并发场景 |
代码示例:
java
// Caffeine 缓存示例
Cache<String, User> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.recordStats()
.build();
// 使用缓存
User user = cache.get("user:1", key -> loadUserFromDB(key));2. 分布式缓存
特点:
- 数据共享:多个应用实例共享缓存
- 可扩展性:支持集群部署
- 网络开销:需要网络访问
- 数据一致性:支持数据同步
适用场景:
- 分布式系统
- 多实例数据共享
- 大规模数据缓存
- 会话存储
常见实现:
| 技术 | 特点 | 适用场景 | 性能 |
|---|---|---|---|
| Redis | 高性能、丰富数据结构 | 通用缓存 | 极高 |
| Memcached | 简单、高性能 | 简单键值缓存 | 高 |
| Hazelcast | Java分布式缓存 | Java应用 | 高 |
| Ignite | 内存数据网格 | 大数据场景 | 高 |
对比分析:
| 特性 | Redis | Memcached | Hazelcast | Ignite |
|---|---|---|---|---|
| 数据结构 | 丰富 | 简单 | 丰富 | 丰富 |
| 持久化 | 支持 | 不支持 | 支持 | 支持 |
| 事务 | 支持 | 不支持 | 支持 | 支持 |
| 集群 | 支持 | 支持 | 支持 | 支持 |
| 性能 | 极高 | 高 | 高 | 高 |
🎯 缓存策略
1. Cache Aside(旁路缓存)
策略流程:
特点:
- 读取流程:先查缓存,未命中查数据库,写入缓存
- 更新流程:更新数据库,删除缓存
- 优点:简单易实现,缓存命中率高
- 缺点:可能出现数据不一致
代码示例:
java
public User getUser(Long id) {
// 先查缓存
User user = cache.get("user:" + id);
if (user != null) {
return user;
}
// 缓存未命中,查询数据库
user = userRepository.findById(id);
if (user != null) {
// 写入缓存
cache.put("user:" + id, user);
}
return user;
}
public void updateUser(User user) {
// 更新数据库
userRepository.update(user);
// 删除缓存
cache.delete("user:" + user.getId());
}2. Write Through(写穿透)
策略流程:
特点:
- 更新流程:先更新缓存,再更新数据库
- 优点:数据一致性好
- 缺点:写操作性能较差
代码示例:
java
public void updateUser(User user) {
// 先更新缓存
cache.put("user:" + user.getId(), user);
// 再更新数据库
userRepository.update(user);
}3. Write Back(写回)
策略流程:
特点:
- 更新流程:只更新缓存,异步批量写入数据库
- 优点:写操作性能极好
- 缺点:数据可能丢失,实现复杂
4. Refresh Ahead(提前刷新)
策略流程:
- 缓存过期前:异步刷新缓存数据
- 优点:减少缓存穿透,提升命中率
- 缺点:可能造成资源浪费
📋 缓存选型建议
1. 选型矩阵
| 场景 | 推荐方案 | 理由 |
|---|---|---|
| 单机应用 | 本地缓存 | 性能最高,无需网络 |
| 分布式应用 | Redis | 功能丰富,生态完善 |
| 简单键值 | Memcached | 简单高效 |
| 大数据量 | Redis Cluster | 支持集群扩展 |
| Java应用 | Caffeine + Redis | 本地+分布式组合 |
2. 选型考虑因素
性能要求:
- 极高性能:本地缓存(Caffeine)
- 高性能:Redis、Memcached
- 中等性能:Hazelcast、Ignite
功能需求:
- 丰富数据结构:Redis、Hazelcast
- 简单键值:Memcached
- 事务支持:Redis、Hazelcast
运维成本:
- 运维简单:Memcached
- 运维中等:Redis
- 运维复杂:Hazelcast、Ignite
成本考虑:
- 开源免费:Redis、Memcached、Caffeine
- 商业授权:Ignite商业版
3. 混合缓存策略
多级缓存架构:
优势:
- 性能最优:本地缓存+分布式缓存
- 减少网络开销:热点数据在本地
- 数据共享:分布式缓存支持数据共享
代码示例:
java
public User getUser(Long id) {
// L1: 本地缓存
User user = localCache.get("user:" + id);
if (user != null) {
return user;
}
// L2: 分布式缓存
user = redisCache.get("user:" + id);
if (user != null) {
// 写入本地缓存
localCache.put("user:" + id, user);
return user;
}
// L3: 数据库
user = userRepository.findById(id);
if (user != null) {
// 写入两级缓存
localCache.put("user:" + id, user);
redisCache.put("user:" + id, user);
}
return user;
}📋 缓存最佳实践
1. 缓存设计
键设计:
- 命名规范:使用前缀,如
user:{id} - 层级清晰:使用分隔符,如
module:entity:id - 避免冲突:使用唯一标识符
值设计:
- 序列化格式:JSON、Protobuf、Hessian
- 数据大小:控制单条数据大小
- 敏感信息:避免缓存敏感信息
2. 缓存过期
过期策略:
- TTL(Time To Live):固定过期时间
- TTI(Time To Idle):访问后重置过期时间
- LRU(Least Recently Used):最近最少使用
- LFU(Least Frequently Used):最少使用频率
过期时间设置:
| 数据类型 | 过期时间 | 理由 |
|---|---|---|
| 热点数据 | 5-30分钟 | 平衡性能和一致性 |
| 配置数据 | 30-60分钟 | 配置变更不频繁 |
| 计算结果 | 1-24小时 | 计算结果相对稳定 |
| 用户会话 | 30分钟-2小时 | 平衡安全和体验 |
3. 缓存穿透、击穿、雪崩
缓存穿透:
- 问题:查询不存在的数据,绕过缓存直击数据库
- 解决方案:布隆过滤器、缓存空值
缓存击穿:
- 问题:热点数据过期,大量请求直击数据库
- 解决方案:互斥锁、永不过期
缓存雪崩:
- 问题:大量缓存同时过期,请求直击数据库
- 解决方案:过期时间随机化、多级缓存
📖 学习资源
视频教程
书籍推荐
- 《Redis设计与实现》 - 深入讲解Redis原理
- 《高性能网站建设指南》 - 网站缓存优化
- 《缓存架构设计》 - 缓存架构最佳实践
- 《Redis实战》 - Redis使用指南
网站资源
工具推荐
- 本地缓存:Caffeine、Guava Cache、Ehcache
- 分布式缓存:Redis、Memcached、Hazelcast
- 缓存监控:RedisInsight、Memcached监控工具
✅ 缓存选型检查清单
- [ ] 缓存类型已确定(本地/分布式)
- [ ] 缓存技术已选型(Redis/Caffeine等)
- [ ] 缓存策略已设计(Cache Aside/Write Through等)
- [ ] 缓存键设计规范
- [ ] 缓存过期策略已设置
- [ ] 缓存穿透/击穿/雪崩已防护
- [ ] 缓存监控已建立