Redis系列 - Redis缓存更新:先更新数据库还是先更新缓存?

Redis系列 - Redis缓存更新:先更新数据库还是先更新缓存?

在更新缓存时,对于更新完数据库,是更新缓存呢,还是删除缓存。又或者是先删除缓存,再更新数据库,其实都会存在一定的问题。

Cache Aside Pattern(旁路缓存模式)

这是最常用的缓存使用方式了。其具体逻辑如下

  • 失效:应用程序先从cache取数据,没有得到,则从数据库中取数据,成功后,放到缓存中。
  • 命中:应用程序从cache中取数据,取到后返回。
  • 更新:先把数据存到数据库中,成功后,再让缓存失效。

我们更新时是先更新数据库,数据库更新成功后再让缓存失效。那么这种方式真的没有问题么?

我们可以考虑一下以下的并发场景:

  1. 缓存key1刚好失效
  2. 请求A发起读请求没有命中缓存去数据库查询,此时查询出来的结果是老的数据
  3. 请求B发起更新请求,先更新数据库,
  4. 请求B让缓存失效
  5. 此时请求A将步骤2中读取出来的老数据写入缓存

以上的并发场景理论上确实会发生导致脏数据的产生,但是在实际的生产环境中出现的概率非常低,因为这个条件需要发生在读缓存时缓存失效,而且并发着有一个写操作。而实际上数据库的写操作会比读操作慢得多,而且还要锁表,而读操作必需在写操作前进入数据库操作,而又要晚于写操作更新缓存,所有的这些条件都具备的概率基本并不大。

先删除缓存,再更新数据库

改方案会导致数据不一致的原因如下:

  1. 请求A进行写操作,删除缓存(并更新数据库,此时还没更新)
  2. 请求B查询发现缓存不存在
  3. 请求B去数据库查询得到旧的值
  4. 请求B将旧的值写入缓存
  5. 此时请求A将新值更新进数据库

这种情况下如果缓存不被更新或者被过期策略淘汰,那么这个数据将永远是脏数据。

先更新数据库,再更新缓存

我们可以看下以下的并发场景:

  1. 线程A和线程B同时去更新数据key1
  2. 线程A更新了数据库数据key1,此时value1
  3. 线程A更新了数据库数据key1,此时value2(最新的数据)
  4. 线程B更新的缓存key1的值为value2
  5. 线程A更新的缓存key1的值为value1

由于线程A的更新数据库操作早于线程B,线程B更新的结果value2才是最新的结果,最终应该把value2放入缓存才符合实际的需求。但是因为网络等原因,B却比A更早更新了缓存。这就导致了脏数据,因此这种方案存在线程安全问题。

先更新缓存,再更新数据库

该方案不做考虑,若先更新缓存,缓存更新成功,但是更新数据库时发生异常导致回滚,那么缓存中的数据无法回滚,导致数据不一致。

 

 

你可能感兴趣的:(缓存-Redis,redis,缓存)