Redis三种常见的缓存更新模式介绍
Redis常见的缓存更新策略有三种,分别是Cache Aside Pattern(旁路缓存模式)、Read/Write Through Pattern(读写穿透模式)以及Write Behind Pattern (异步缓存写入模式)三种。三种模式各有优劣,不存在最佳模式,根据具体的业务场景选择适合自己的缓存读写模式即可。以下将分别介绍三种模式。
一、Cache Aside Pattern(旁路缓存模式)
Cache Aside Pattern 是我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。
Cache Aside Pattern 中服务端需要同事维系数据库(后文简称db)和缓存(后文简称cache),并且是以db的结果为准。
下面我们来看一下这个策略模式下的缓存读写步骤。
写:
- 先更新db;
- 直接删除cache。
如下图所示:
读:
- 从cache中读取数据,读取到就直接返回;
- cache中读取不到的话,就从db中读取数据返回;
- 再把db中读取到的数据放到cache中。
如下图所示:
那么问题来了,为什么是删除cache,而不是更新cache呢?
主要原因有两点:
- 对服务端资源造成浪费: 删除cache更加直接,这是因为cache中存放的一些数据需要服务端经过大量的计算才能得出,会小号服务端的紫东苑,是一笔不小的开销。如果频繁修改db, 就可能会导致需要频繁更新cache,二cache中的数据可能都没有被反问到。
- 产生数据不一致的问题: 并发场景下,更新cache产生数据不一致性问题的概率会更大。
然后另外一个问题, 在写数据的过程中,能否先删除cache,后更新db呢?
答案可能是不行,因为这样可能会导致造成数据库(db)和缓存(Cache)数据不一致的问题。
举个例子:请求1先写数据A,请求2虽有读数据A的话,就很有可能产生数据不一致性的问题。这个过程可以简单描述为:
- 请求1先把cache中的A数据删除;
- 请求2从db中读取数据;
- 请求1再把db中的A数据更新;
这就会导致请求2读取到的是旧值。
那么 如果是在写数据的过程中,先更新db,后删除cache就没有问题了吗?
理论上来说还是可能会出现数据不一致的问题,不过概率非常小,因为缓存的写入速度比数据库的写入速度快很多。
举个例子:请求1先读取数据A,请求2随后写数据A,并且数据A在请求1请求之前不在缓存中的话,也有可能产生数据不一致性的问题。这个过程可以简单描述为:
- 请求1从db读取数据A;
- 请求2更新db中的数据A(此时缓存中无数据A,故不用执行删除缓存操作);
- 请求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)
如下图:
读(Read Through):
- 从cache中读取数据,读取到就直接返回
- 读取不到的话,先从db加载,写入到cache后返回响应。
如下图:
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常见的三种缓存更新策略的一个介绍。