Read Through 模式:缓存的透明读取策略
什么是 Read Through 模式
Read Through(穿透读取)是一种缓存策略,它将缓存和数据库的交互逻辑封装在缓存抽象层中,对业务代码完全透明。当应用程序请求数据时,直接向缓存层发起请求;如果缓存未命中,缓存层自动负责从数据库加载数据并回填缓存,最后将结果返回给调用方。
这与传统的 Cache Aside(旁路缓存)模式不同——在 Cache Aside 中,应用程序需要自己维护缓存和数据库之间的交互逻辑。
Read Through 的工作流程
- 应用程序调用缓存抽象层获取某个 key 的值
- 缓存层检查 key 是否存在
– 命中:直接返回缓存数据
– 未命中:缓存层从配置的数据源(通常是数据库)加载数据,将数据写入缓存,然后返回给应用程序 - 应用程序对整个过程无感知,仿佛缓存层本身就拥有所有数据
核心优势
- 简化业务代码:业务方只需要和缓存层交互,不需要关心回源逻辑、缓存过期策略等复杂细节
- 集中管理策略:缓存加载策略、过期时间、序列化方式等统一在缓存层配置,修改策略无需修改业务代码
- 防止缓存穿透:可以在缓存抽象层内置布隆过滤器或空值缓存,统一防护
- 便于统一监控:缓存命中率、加载延迟等指标集中采集
典型实现
Guava Cache / Caffeine
LoadingCache<String, User> cache = Caffeine.newBuilder()
.maximumSize(10_000)
.expireAfterWrite(10, TimeUnit.MINUTES)
.build(key -> userDao.findById(key));
// 业务代码直接调用,无需关心回源逻辑
User user = cache.get("user_1001");
Redis 配合封装层
public class ReadThroughCache<T> {
private final RedisTemplate redis;
private final Class<T> type;
private final Function<String, T> loader;
public T get(String key) {
Object value = redis.opsForValue().get(key);
if (value != null) return (T) value;
// 缓存层自动加载
T loaded = loader.apply(key);
if (loaded != null) {
redis.opsForValue().set(key, loaded, 30, TimeUnit.MINUTES);
}
return loaded;
}
}
Read Through vs Cache Aside
| 特点 | Read Through | Cache Aside |
|---|---|---|
| 代码耦合 | 业务方只接触缓存层 | 业务方要维护缓存+DB逻辑 |
| 一致性控制 | 集中在缓存层 | 散落在业务代码 |
| 学习成本 | 较低(封装好即可) | 较高(需理解回源时机) |
| 灵活性 | 受限于封装层能力 | 业务方完全掌控 |
注意事项
- 缓存雪崩风险:如果大量 key 同时过期,大量请求会穿透到数据库。建议设置随机过期时间,或使用本地+远程多级缓存
- 热点 key 问题:某个 key 过期瞬间会有大量并发请求同时触发回源。建议配合互斥锁(如 SETNX)防止并发回源
- 空值处理:数据库中不存在的 key,如果不做特殊处理,每次请求都会穿透到数据库。建议在缓存层自动缓存空值并设置较短过期时间
- 缓存更新一致性:Read Through 配合数据库更新时,需要结合 Write Through 或异步失效机制,否则会出现缓存与数据库不一致
面试要点
- 理解 Read Through 的核心思想:缓存层承担了数据加载职责,对业务透明
- 能对比 Read Through 和 Cache Aside 的优缺点
- 能说清 Read Through 模式下的缓存雪崩、穿透、击穿的应对方案
- 了解 Caffeine、Ehcache 等本地缓存的 LoadingCache 实现
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END


暂无评论内容