缓存读写策略:CacheAside、Read/WriteThrough及WriteBack策略

1.缓存读写策略

对于缓存的读写来说,通常存在三种使用方式,也就是缓存的三种读写策略:CacheAside、Read/WriteThrough及WriteBack策略。

2.最简单的缓存更新策略

先假设一个常见的缓存使用场景:有一个电商系统首页调用商品接口展示商品内容及价格,为了应对高QPS,该接口使用Redis缓存商品信息,减缓对数据库的压力。
在这里插入图片描述

当不考虑缓存读写策略时,最容易想到的读缓存流程为:

  • 首先查询缓存层,如果商品存在缓存则直接返回结果
  • 若不存在缓存,则读取数据库获取商品数据
    当出现商品数据变更时,直接更新缓存:
  • 先修改数据库中的商品数据
  • 若缓存不存在,则写入商品数据缓存
  • 若缓存存在,则修改缓存中的商品数据
    在这里插入图片描述

这种最简单的缓存操作策略在低并发的操作下可以使用,但是在高并发操作下会极大概率出现缓存不一致问题。假设此时有两台服务器正在执行定时任务,依次发送A、B两个请求对同一商品价格进行变更,此时预期的商品价格缓存应该以最后的变更请求价格为准,也就是20元:

  • A请求需要将商品价格变更为10元
  • A请求将商品缓存价格变更为10元
  • B请求需要将商品价格变更为20元
  • B请求将商品缓存价格变更为20元

但是实际上可能出现数据库修改操作与缓存修改时序不一致问题:

  • A请求需要将商品价格变更为10元
  • B请求需要将商品价格变更为20元
  • B请求将商品缓存价格变更为20元
  • A请求将商品缓存价格变更为10元
    在这里插入图片描述

对于这种策略来说,引发缓存数据不一致的核心原因是变更数据库和变更缓存是两个独立的操作,而我们并没有对操作做任何的并发控制。那么当两个线程并发更新它们的时候,就会因为写入顺序的不同造成数据的不一致,引发更新丢失问题。

3.CacheAside策略

CacheAside策略即旁路缓存策略,是工程上最常用的一种缓存读写策略。相较于基础的缓存读写策略来说,变动如下:

  • 读缓存策略:若缓存存在则直接返回缓存数据,若缓存不存在,则在读数据库后回写缓存。
  • 写缓存策略:在更新数据库后不更新缓存,而是直接删除缓存。

CacheAside策略中缓存中的数据永远是读取最新的数据库数据为准,所以缓存数据不一致率较低。
在这里插入图片描述

对于高并发系统来说,数据库和缓存系统都可能出现写入数据失败的情况,一般来说对于数据库写入的失败是fail fast的,而对于缓存的删除失败是fail safe的,因为删除缓存是主干流程中的非核心流程,不能因为非核心流程影响接口整体的可用率。

但是在删除缓存失败时,CacheAside策略就会出现一定的数据不一致,此时如果对这类数据不一致容忍程序较低,可以外接MQ,将删除缓存失败的商品信息写入,并由兜底消费程序来删除缓存,避免长时间数据不一致,达到最终一致性。
在这里插入图片描述

4.Read/WriteThrough策略

该策略又被称为读穿/写穿策略,和CacheAside策略的缓存数据与数据库数据为准不同,该策略的核心是用户只与缓存层交互,由缓存层与数据库通信,写入或读取数据。

  • 在读取数据时,通过缓存层进行读取,若缓存存在则直接返回,若不存在则由缓存层拉取数据库数据到缓存中并返回。
  • 在写数据时,通过缓存层进行写入,若缓存存在则直接写入缓存中并同步到数据库,若不存在则写入数据库中。
    在这里插入图片描述

一般读穿/写穿策略需要由特定的库来支持,比如Guava Cache等,所以这种策略通常用于本地缓存时所使用,比如在读穿时,可以将获取到的数据写入本地缓存中,并且设置极短的过期时间,这样可以很好地抵抗高并发的QPS。但是使用本地缓存时需要注意上游调用时开启一致性哈希,避免服务器扩容导致本地缓存全部失效。

5.WriteBack策略

写回策略是计算机存储系统中常用的一种缓存读写策略,比如PageCache的读写、日志的读写及CPU Cache的读写等,主要是利用读写内存来替代直接读写磁盘。

  • 在读取数据时,如果存在缓存则直接返回,如果缓存不存在时则寻找一个可用的缓存块,如果该该缓块被标识为“脏”的,则将这个脏块之前的数据写入存储系统中,然后从存储系统中加载数据到缓存中。
  • 在写入数据时,如果存在缓存则写入缓存数据,并且将之前的缓存块标识为“脏”,如果不存在缓存则写入存储系统,并且也写入缓存。
    在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/pbrlovejava/article/details/125953394