驽马十驾 驽马十驾

驽马十驾,功在不舍

目录
缓存更新策略(二)
/  

缓存更新策略(二)

上一篇文章提到了2个观点:

  1. 缓存必须设置过期时间,可以保证数据的最终一致性
  2. 在数据发生变更的同时,重新建立缓存是没有必要的操作。

同时提到了数据更新的时候,删除缓存和更新DB,哪个操作应该先行了?

本文同上一篇文章一样,假设在不为缓存添加过期时间极端情况下,进行思考。

  • 先删除缓存,再更新DB
  • 先更新DB,再删除缓存

先删除缓存,再更新DB

假如:A线程在对数据X进行更新操作,B线程在对数据X进行查询操作。

  1. A线程因为要更新DB,所以先删除数据X的缓存
  2. B线程紧接着对数据X进行查询,发现缓存已经不存在,于是重新建立当前DB数据的缓存。
  3. A线程此时才对数据X进行DB的更新。

上述 1 --> 2 --> 3 是按照时间线进行的,大家发现问题没?

因为写数据库的耗时比查询和建立缓存更耗时,所以该情况发生的概率有的。

第2步建立的缓存还是老数据的缓存,第三步才是对数据进行的更新。

那么有办法解决这个问题吗?我认为只能避免,在不加缓存过期时间的情况下无法解决。

有一个避免(不是解决)的办法是在上述步骤后再加一个步骤4:删除缓存

即使在这个过程中,有查询操作建立了缓存,对其进行删除操作应该也能避免脏数据吧。表面来看确实如此,但是细细分析,确还是有问题,再加入了第4步二次删除缓存的情况下,进行如下操作:

  1. A线程因为要更新DB,所以先删除数据X的缓存
  2. B线程紧接着对数据X进行查询,发现缓存已经不存在,准备建立当前DB数据的缓存(还没开始,只是准备)。
  3. A线程此时才对数据X进行DB的更新。
  4. A线程二次删除缓存(缓存如果没有,则跳过此步骤)。
  5. B线程在第2个步骤的准备建立缓存操作开始:建立缓存。此时缓存是脏数据。

通过直接进行二次删除也会有问题。

那么继续分析,我再进行二次删除前睡眠一段时间,待2步骤准备建立缓存的操作结束。这个方案理论上是可以的,但是你告诉我,睡眠多长时间?

综上:即使通过2次删除缓存也没有很好的解决此问题。

先更新DB,再删除缓存(推荐)

正如小标题后方括号的标注而言,这个方案是我最推荐的方案:先更新DB,再删除缓存。

先更新DB,在还没有删除的这段时间内,即使有操作读取到了缓存中的脏数据,那也只是一时的,因为随后肯定会对缓存进行删除。

该方案在没有缓存过期时间的情况下,应该是最优的方案了。

其实该方案同样存在问题:

  1. A线程读取到老数据,准备建立缓存。
  2. B线程更新DB
  3. B线程删除缓存。
  4. A线程开始建立缓存(老数据的缓存)

因为写数据操作比缓存建立耗时,所以该情况发生的概率很小。

为了保证缓存的一致性,该方案只需要保证删除缓存成功即可:

  1. 通过Retry进行缓存删除,直到删除成功为知。
  2. 将删除操作放到MQ中,让消费者消费,直到成功为止。
  3. .....

方案很多,不再一一列举。

结论

  1. 缓存必须加上过期时间,防止偶然的脏数据。
  2. 数据发生变更时候,不应该是重新建立缓存,而应该是直接删除缓存。
  3. 缓存删除应该在DB更新后进行。
不积跬步,无以至千里。不积小流,无以成江海。