docs/database/redis/3-commonly-used-cache-read-and-write-strategies.md
看到很多小伙伴简历上写了“熟练使用缓存”,但是被我问到“缓存常用的 3 种读写策略”的时候却一脸懵逼。
在我看来,造成这个问题的原因是我们在学习 Redis 的时候,可能只是简单写了一些 Demo,并没有去关注缓存的读写策略,或者说压根不知道这回事。
但是,搞懂 3 种常见的缓存读写策略对于实际工作中使用缓存以及面试中被问到缓存都是非常有帮助的!
下面介绍到的三种模式各有优劣,不存在最佳模式,根据具体的业务场景选择适合自己的缓存读写模式。
这是我们日常开发中最常用、最经典的一种模式,几乎是互联网应用缓存方案的事实标准,尤其适合读多写少的业务场景。
这个模式之所以被称为**“旁路”(Aside),是因为应用程序的写操作完全绕过了缓存,直接操作数据库**。
应用程序扮演了数据流转的“指挥官”,需要同时维护 Cache 和 DB 两个数据源。
下面我们来看一下这个策略模式下的缓存读写步骤。
写操作 :
简单画了一张图帮助大家理解写的步骤。
读操作:
简单画了一张图帮助大家理解读的步骤。
你仅仅了解了上面这些内容的话是远远不够的,我们还要搞懂其中的原理。
比如说面试官很可能会追问:
接下来我会以此分析解答这些问题。
1. 为什么写操作是“先更新 DB,后删除 Cache”?顺序能反过来吗?
答: 绝对不能。如果“先删 Cache,后更新 DB”,在高并发下会引入经典的数据不一致问题。
2. 那“先更新 DB,后删除 Cache”就绝对安全吗?
答案: 也不是绝对安全的!因为这样也可能会造成 数据库和缓存数据不一致的问题。
3. 为什么是“删除 Cache”,而不是“更新 Cache”?
当然,这一切都建立在一个重要的前提之上:我们缓存的数据,是可以通过数据库进行确定性重建的,并且业务上可以容忍从‘缓存删除’到‘下一次读取并回填’之间这个极短时间窗口内的数据不一致。
现在我们再来分析一下 Cache Aside Pattern 的缺陷。
缺陷 1:首次请求数据一定不在 Cache 的问题
解决办法:对于访问量巨大的热点数据,可以在系统启动或低峰期进行缓存预热。
缺陷 2:写操作比较频繁的话导致 Cache 中的数据会被频繁被删除,这样会影响缓存命中率 。
解决办法:
在这种模式下,应用程序将Cache 视为唯一的、主要的存储。所有的读写请求都直接打向 Cache,而 Cache 服务自身负责与 DB 进行数据同步。
对应用程序透明,应用开发者无需关心 DB 的存在。
这种缓存读写策略小伙伴们应该也发现了在平时在开发过程中非常少见。抛去性能方面的影响,大概率是因为我们经常使用的分布式缓存 Redis 本身并没有提供 Cache 将数据写入 DB 的功能,需要我们在业务侧或中间件里自己实现。
写(Write Through):
简单画了一张图帮助大家理解写的步骤。
读(Read Through):
简单画了一张图帮助大家理解读的步骤。
Read-Through 实际只是在 Cache-Aside 之上进行了封装。在 Cache-Aside 下,发生读请求的时候,如果 Cache 中不存在对应的数据,是由客户端自己负责把数据写入 Cache,而 Read Through 则是 Cache 服务自己来写入缓存的,这对客户端是透明的。
从实现角度看,Read-Through 本质上是把 Cache-Aside 中“读 Miss → 读 DB → 回填 Cache”的逻辑,下沉到了缓存服务内部,对客户端透明。
和 Cache Aside 一样, Read-Through 也有首次请求数据一定不再 Cache 的问题,对于热点数据可以提前放入缓存中。
Write Behind(也常被称为 Write-Back) Pattern 和 Read/Write Through Pattern 很相似,两者都是由 Cache 服务来负责 Cache 和 DB 的读写。
但是,两个又有很大的不同:Read/Write Through 是同步更新 Cache 和 DB,而 Write Behind 则是只更新缓存,不直接更新 DB,而是改为异步批量的方式来更新 DB。
写操作 (Write Behind):
这种模式对数据一致性带来了挑战(例如:Cache 中的数据还没来得及写回 DB,系统就宕机了),因此不适用于需要强一致性的场景(如交易、库存)。
但是,它的异步和批量特性,带来了无与伦比的写性能。它在很多高性能系统中都有广泛应用: