常用的3种缓存读写策略

Cache Aside Pattern(旁路缓存模式)

  • 先更新 DB;
  • 然后直接删除 cache 。

:

  • 从 cache 中读取数据,读取到就直接返回;
  • cache中读取不到的话,就从 DB 中读取数据返回;
  • 再把数据放到 cache 中。

Read/Write Through Pattern(读写穿透)

服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 DB,从而减轻了应用程序的职责(Redis 里没有提供 cache 将数据写入DB的功能)实际是在 Cache-Aside Pattern 之上进行了封装,让 cache 服务自己来写入缓存,对客户端是透明的。

写(Write Through):

  • 先查 cache,cache 中不存在,直接更新 DB。
  • cache 中存在,则先更新 cache,然后 cache 服务自己更新 DB(同步更新 cache 和 DB)。

读(Read Through):

  • 从 cache 中读取数据,读取到就直接返回 。
  • 读取不到的话,先从 DB 加载,写入到 cache 后返回响应。

Write Behind Pattern(异步缓存写入)

和 Read/Write Through Pattern 很相似,都是由 cache 服务来负责 cache 和 DB 的读写。Read/Write Through 是同步更新 cache 和 DB,而 Write Behind Caching 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。这种方法下的 DB 的写性能非常高,非常适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量、点赞量。

缓存+数据库的操作顺序

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

业务无法保证两个写操作都成功。当更新缓存成功,更新数据库失败时,数据库中就是旧数据。

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

在多线程并发时,会出现不一致行为。假设请求 A 先操作数据库,请求 B 后操作数据库,但是可能存在请求 B 先写缓存,请求 A 后写缓存的情况,从而导致数据库与缓存之间的数据不一致。

先写数据库,再删除缓存

业务要保证写数据库和删除缓存是一个原子操作,否则写数据成功,删除缓存失败就会出现数据不一致的问题。

请求1从DB读数据A->请求2写更新数据 A 到数据库并删除cache中的A数据->请求1将数据A写入cache

先删缓存, 再写数据库

先删除缓存,再写数据库前,可能另外一个读请求, 在缓存未命中时,从数据库获取到旧值,将其放到缓存。这时缓存中是旧值,数据库是新值。

请求1先把cache中的A数据删除 -> 请求2从DB中读取数据->请求1再把DB中的A数据更新

保证一致性的解决方案

采用延时双删策略

在写库前后删除缓存中数据,并且给缓存数据设置合理的过期时间, 从而可以保证最终一致性。

写流程如下:

  1. 先删除缓存,再写数据库
  2. 休眠一段时间(比如500毫秒)
  3. 再次删除缓存

防止在写请求删除缓存但还未成功写入数据库后,读请求可能将旧值加载到缓存。确保读请求结束时,写请求可以删除读请求造成的缓存脏数据。

可能存在的问题:

  • 休眠一段时间可能会对性能造成影响;
  • 在第二次删除缓存失败后,会导致数据不一致,需要业务方实现重试删除机制。

异步更新缓存策略(基于订阅binlog的同步机制)

使用一个异步同步组件,通过解析从库的 binlog 获取数据,并通过消息队列将其串行化写入到缓存。

写流程:

  1. 先删除缓存,再写数据库
  2. 额外组件通过解析从库 binlog, 将写操作发送到消息队列
  3. 缓存从消息队列中消费,更新缓存(订阅DB的数据操作记录binlog,来更新到Redis)

读流程:

  1. 先从缓存读取(热数据基本都在Redis)
  2. 如果缓存未命中,从数据库读取,将数据发送到消息队列。
  3. 缓存从消息队列消费,更新缓存。

一旦DB中产生了新的写入、更新、删除等操作,就可以把binlog相关的消息通过消息队列推送至Redis,Redis再根据binlog中的记录,对Redis进行更新。将并行化的操作串行化,从而解决了并发问题。但是也导致缓存相对主库落后延迟较大。

混合存储优化方案

  • 引入版本号
  • Redis 中缓存全量的 Key(防缓存穿透,在布隆过滤器的基础上需要增加删除及动态扩展的功能)
  • 用户请求统一由缓存服务,请求不会转发给后端数据库(防缓存雪崩)
  • 缓存异步持久化到DB
  • Redis 限速,未命中缓存时阻塞当前客户端(防缓存击穿与一致性问题)

Reference