缓存更新
缓存更新
三种模式对比:
模式名称 | 数据库与缓存一致性 | 性能 | 可靠性 | 特点 |
---|---|---|---|---|
Cache Aside 缓存到旁边 | 最终一致 | 高 | 中 | 简单易实现,适合读多写少场景 |
Read/Write Through 读/写 | 强一致 | 中 | 高 | 所有操作都通过缓存代理数据库 |
Write Behind 后写 | 最终一致 | 高 | 中 | 延迟写入数据库,性能高但复杂 |
Cache Aside 模式-旁路缓存
原理:
- 读取时:先查缓存,缓存没有则查数据库,并将结果写回缓存。
- 写入时:直接更新数据库,同时删除或更新缓存。
适用场景:
- 读多写少
- 对一致性要求不高,可接受短暂不一致
查询:
更新数据
代码流程:
java
public class CacheAsideExample {
private final RedisTemplate<String, String> redisTemplate;
private final DataDao dataDao;
public CacheAsideExample(RedisTemplate<String, String> redisTemplate, DataDao dataDao) {
this.redisTemplate = redisTemplate;
this.dataDao = dataDao;
}
// 读取数据(优先从缓存)
public String getData(String key) {
try {
String cached = redisTemplate.opsForValue().get(key);
if (cached != null) {
return cached;
}
String dbData = dataDao.getFromDB(key); // 查询数据库
if (dbData != null) {
redisTemplate.opsForValue().set(key, dbData, 60, TimeUnit.SECONDS); // 写入缓存
}
return dbData;
} catch (Exception e) {
// 记录日志或降级处理
return dataDao.getFromDB(key); // 可选降级方案
}
}
// 更新数据(更新数据库 + 删除缓存)
public void updateData(String key, String newValue) {
try {
dataDao.updateDB(key, newValue); // 更新数据库
redisTemplate.delete(key); // 删除缓存
} catch (Exception e) {
// 日志记录或补偿机制
}
}
}
Read / Write Through 模式-读穿 / 写穿
原理:
- 所有读写请求都经过缓存层。
- 如果缓存中没有数据,由缓存组件自动从数据库加载。
- 写操作时,缓存和数据库同步更新。
这种模式常用于封装了缓存逻辑的服务层(如使用 Spring Cache 或 Caffeine),也可以自己实现。
适用场景:
- 要求缓存和数据库强一致性
- 不希望业务代码处理缓存细节
查询:
与CahcheAside模式一致
更新数据:
代码流程:
java
ublic class CacheService {
private final RedisTemplate<String, User> redisTemplate;
private final UserMapper userMapper;
public CacheService(RedisTemplate<String, User> redisTemplate, UserMapper userMapper) {
this.redisTemplate = redisTemplate;
this.userMapper = userMapper;
}
// Read Through:读取用户信息
public User getUserById(Long userId) {
String cacheKey = "user:" + userId;
// 先查缓存
User user = redisTemplate.opsForValue().get(cacheKey);
if (user == null) {
// 缓存未命中,查询数据库
user = userMapper.selectById(userId);
if (user != null) {
// 写入缓存
redisTemplate.opsForValue().set(cacheKey, user, 60, TimeUnit.SECONDS);
}
}
return user;
}
// Write Through:更新用户信息
public void updateUser(User user) {
String cacheKey = "user:" + user.getId();
// 同步更新数据库
userMapper.update(user);
// 同步更新缓存
redisTemplate.opsForValue().set(cacheKey, user, 60, TimeUnit.SECONDS);
}
}
Write Behind 模式-异步写入 / 回写
原理:
- 写操作只更新缓存,标记为“脏”。
- 后台线程定期将“脏”数据批量写入数据库。
- 优点是高性能,缺点是可能丢失数据。
- 在 Write Behind 模式中,缓存是主数据源,数据库只是最终持久化的目标。
适用场景:
- 对性能要求极高
- 可容忍一定数据丢失风险
查询:
与CacheAside一致
更新数据:
代码流程:
java
public class WriteBehindCache {
private final Map<String, String> cache = new ConcurrentHashMap<>();
private final Map<String, String> writeQueue = new ConcurrentHashMap<>();
private final Database database;
private final ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(1);
public WriteBehindCache(Database database) {
this.database = database;
scheduleFlush();
}
public void set(String key, String value) {
cache.put(key, value);
writeQueue.put(key, value); // 加入待写队列
}
public String get(String key) {
return cache.getOrDefault(key, "Not Found");
}
private void scheduleFlush() {
scheduler.scheduleAtFixedRate(this::flushToDatabase, 5, 5, TimeUnit.SECONDS);
}
private void flushToDatabase() {
for (Map.Entry<String, String> entry : writeQueue.entrySet()) {
database.updateDB(entry.getKey(), entry.getValue()); // 模拟写入 DB
}
writeQueue.clear(); // 清空队列
}
}
对比
模式名称 | 数据库与缓存一致性 | 性能 | 可靠性 | 特点 |
---|---|---|---|---|
Cache Aside 缓存到旁边 | 最终一致 | 高 | 中 | 简单易实现,适合读多写少场景 |
Read/Write Through 读/写 | 强一致 | 中 | 高 | 所有操作都通过缓存代理数据库 |
Write Behind 后写 | 最终一致 | 高 | 中 | 延迟写入数据库,性能高但复杂 |