Redis-缓存更新策略

Redis三种常见的缓存更新模式介绍

Redis常见的缓存更新策略有三种,分别是Cache Aside Pattern(旁路缓存模式)、Read/Write Through Pattern(读写穿透模式)以及Write Behind Pattern (异步缓存写入模式)三种。三种模式各有优劣,不存在最佳模式,根据具体的业务场景选择适合自己的缓存读写模式即可。以下将分别介绍三种模式。

一、Cache Aside Pattern(旁路缓存模式)

Cache Aside Pattern 是我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。
Cache Aside Pattern 中服务端需要同事维系数据库(后文简称db)和缓存(后文简称cache),并且是以db的结果为准。

下面我们来看一下这个策略模式下的缓存读写步骤。

写:

  1. 先更新db;
  2. 直接删除cache。

如下图所示:

Redis-缓存更新策略_第1张图片
读:

  1. 从cache中读取数据,读取到就直接返回;
  2. cache中读取不到的话,就从db中读取数据返回;
  3. 再把db中读取到的数据放到cache中。

如下图所示:
Redis-缓存更新策略_第2张图片

那么问题来了,为什么是删除cache,而不是更新cache呢?

主要原因有两点:

  1. 对服务端资源造成浪费: 删除cache更加直接,这是因为cache中存放的一些数据需要服务端经过大量的计算才能得出,会小号服务端的紫东苑,是一笔不小的开销。如果频繁修改db, 就可能会导致需要频繁更新cache,二cache中的数据可能都没有被反问到。
  2. 产生数据不一致的问题: 并发场景下,更新cache产生数据不一致性问题的概率会更大。

然后另外一个问题, 在写数据的过程中,能否先删除cache,后更新db呢?

答案可能是不行,因为这样可能会导致造成数据库(db)和缓存(Cache)数据不一致的问题。

举个例子:请求1先写数据A,请求2虽有读数据A的话,就很有可能产生数据不一致性的问题。这个过程可以简单描述为:

  1. 请求1先把cache中的A数据删除;
  2. 请求2从db中读取数据;
  3. 请求1再把db中的A数据更新;

这就会导致请求2读取到的是旧值。

那么 如果是在写数据的过程中,先更新db,后删除cache就没有问题了吗

理论上来说还是可能会出现数据不一致的问题,不过概率非常小,因为缓存的写入速度比数据库的写入速度快很多。

举个例子:请求1先读取数据A,请求2随后写数据A,并且数据A在请求1请求之前不在缓存中的话,也有可能产生数据不一致性的问题。这个过程可以简单描述为:

  1. 请求1从db读取数据A;
  2. 请求2更新db中的数据A(此时缓存中无数据A,故不用执行删除缓存操作);
  3. 请求1将数据A写入cache。

这就会导致cache中存放的其实是旧值。

现在我们再来分析一下 Cache Aside Pattern 的缺陷。

缺陷1:首次请求数据一定不在cache的问题

解决方法:可以将热点数据提前放入cache中。

缺陷2: 写操作比较频繁的话导致cache中的数据会被频繁删除,这样会影响缓存命中率。

解决方法:

  • 数据库和缓存数据强一致场景 :更新 db 的时候同样更新 cache,不过我们需要加一个锁/分布式锁来保证更新 cache 的时候不存在线程安全问题。
  • 可以短暂地允许数据库和缓存数据不一致的场景 :更新 db 的时候同样更新 cache,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。

Read/Write Through Pattern(读写穿透模式)

Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。cache 服务负责将此数据读取和写入 db,从而减轻了应用程序的职责。

这种缓存读写策略大家应该也发现了在平时在开发过程中非常少见。抛去性能方面的影响,大概率是因为我们经常使用的分布式缓存 Redis 并没有提供 cache 将数据写入 db 的功能。

写(Write Through):

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

如下图:
Redis-缓存更新策略_第3张图片

读(Read Through):

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

如下图:

Redis-缓存更新策略_第4张图片

Read-Through Pattern 实际只是在 Cache-Aside Pattern 之上进行了封装。在 Cache-Aside Pattern 下,发生读请求的时候,如果 cache 中不存在对应的数据,是由客户端自己负责把数据写入 cache,而 Read Through Pattern 则是 cache 服务自己来写入缓存的,这对客户端是透明的。

和 Cache Aside Pattern 一样, Read-Through Pattern 也有首次请求数据一定不再 cache 的问题,对于热点数据可以提前放入缓存中。

Write Behind Pattern (异步缓存写入模式)

Write Behind Pattern 和 Read/Write Through Pattern 很相似,两者都是由 cache 服务来负责 cache 和 db 的读写。

但是,两个又有很大的不同:Read/Write Through 是同步更新 cache 和 db,而 Write Behind 则是只更新缓存,不直接更新 db,而是改为异步批量的方式来更新 db。

很明显,这种方式对数据一致性带来了更大的挑战,比如 cache 数据可能还没异步更新 db 的话,cache 服务可能就就挂掉了。

这种策略在我们平时开发过程中也非常非常少见,但是不代表它的应用场景少,比如消息队列中消息的异步写入磁盘、MySQL 的 Innodb Buffer Pool 机制都用到了这种策略。

Write Behind Pattern 下 db 的写性能非常高,非常适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量、点赞量。

以上便是对Redis常见的三种缓存更新策略的一个介绍。

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