在分布式系统的缓存使用场景中,缓存和数据库的数据一致性是需要重点解决的问题,尤其是并发环境下的删除缓存与更新数据库操作,操作顺序的不同会直接导致不同的数据一致性结果,两种常见操作顺序各有其适用场景和潜在风险。

两种常见操作顺序分析
方案一:先删除缓存,再更新数据库
这种方案的操作逻辑是,当业务需要更新数据时,首先删除缓存中的旧数据,然后再执行数据库的更新操作。
在单线程场景下,这个方案看起来没有问题,但在并发场景下会出现数据不一致的问题。假设有两个并发请求,一个是更新请求A,一个是查询请求B:
- 请求A先执行删除缓存操作,此时缓存中已无对应数据
- 请求B查询数据,发现缓存不存在,于是去数据库查询旧数据
- 请求B将查询到的旧数据写入缓存
- 请求A才执行完数据库的更新操作,此时数据库已经是新数据,但缓存中是旧数据,出现数据不一致
对应的代码逻辑示例如下:
// 先删除缓存再更新数据库示例
public void updateDataFirstDeleteCache(String key, Object newData) {
// 1. 删除缓存
redisTemplate.delete(key);
// 2. 更新数据库,这里模拟数据库更新操作
userMapper.updateById(newData);
}
方案二:先更新数据库,再删除缓存
这种方案的操作逻辑是,先执行数据库的更新操作,完成后再删除缓存中的旧数据。
同样在并发场景下分析,假设更新请求A和查询请求B并发执行:
- 请求B查询缓存,缓存中存在数据,直接返回旧数据(此时还没到更新数据库的步骤,属于正常情况)
- 请求A执行更新数据库操作,将数据库数据更新为新值
- 请求A删除缓存
- 后续新的查询请求会去数据库查询新数据,再写入缓存,数据恢复一致
这种方案出现数据不一致的概率极低,只有在更新数据库成功但删除缓存失败的极端情况下才会出现问题,对应的代码示例如下:
// 先更新数据库再删除缓存示例
public void updateDataFirstUpdateDB(String key, Object newData) {
// 1. 更新数据库
userMapper.updateById(newData);
// 2. 删除缓存
redisTemplate.delete(key);
}
并发场景下的风险优化
先删除缓存方案的优化
针对先删除缓存再更新数据库的并发不一致问题,可以使用延迟双删策略优化。也就是在更新数据库完成后,延迟一段时间再次删除缓存,避免查询请求写入旧数据到缓存。
代码示例如下:
// 延迟双删优化示例
public void updateDataWithDelayDoubleDelete(String key, Object newData) {
// 1. 第一次删除缓存
redisTemplate.delete(key);
// 2. 更新数据库
userMapper.updateById(newData);
// 3. 延迟500ms再次删除缓存,延迟时间根据业务查询耗时调整
try {
Thread.sleep(500);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
redisTemplate.delete(key);
}
先更新数据库方案的优化
针对先更新数据库再删除缓存的删除失败问题,可以引入重试机制。当删除缓存失败时,将删除操作放入消息队列,异步重试删除,直到删除成功。
代码示例如下:
// 删除缓存重试机制示例
public void updateDataWithRetryDelete(String key, Object newData) {
// 1. 更新数据库
userMapper.updateById(newData);
// 2. 尝试删除缓存
boolean deleteSuccess = redisTemplate.delete(key);
if (!deleteSuccess) {
// 删除失败,将key放入消息队列重试
retryDeleteCacheQueue.offer(key);
}
}
// 异步重试线程示例
class RetryDeleteThread extends Thread {
@Override
public void run() {
while (true) {
String key = retryDeleteCacheQueue.poll();
if (key != null) {
try {
redisTemplate.delete(key);
} catch (Exception e) {
// 重试失败再次放入队列
retryDeleteCacheQueue.offer(key);
}
}
}
}
}
方案选择建议
如果业务对数据一致性要求不是特别高,且更新操作频率较低,优先选择先更新数据库再删除缓存的方案,实现简单,出现不一致的概率极低。
如果业务更新操作非常频繁,且对数据一致性要求较高,可以考虑使用先删除缓存+延迟双删的方案,或者结合分布式锁保证更新和查询操作的互斥性,避免并发冲突。
无论选择哪种方案,都建议做好缓存删除失败的监控和告警,及时发现数据不一致问题,避免影响业务正常运行。