热衷学习,热衷生活!😄
沉淀、分享、成长,让自己和他人都能有所收获!😄
一、Redis三种常用的缓存读写策略
Redis有三种读写策略分别是:旁路缓存模式策略、读写穿透策略、异步缓存写入策略。
这三种缓存读写策略各有优势,不存在最佳,需要我们根据实际的业务场景选择最合适的。
二、旁路缓存模式(Cache Aside Pattern)
旁路缓存模式是我们平时使用比较多的一个缓存读写模式,比较适合读请求比较多的场景。
旁路缓存模式中服务端需要同时维护DB
和Cache
,并且是以DB
的结果为准。
读写步骤
写:
- 先更新
DB
。 - 然后直接删除
cache
。
如下图:
读:
- 从
cache
中读取数据,读取到就直接返回。 cache
中读取不到的话,就从DB
读取返回。- 再把数据写到
cache
中。
如下图:
自我思考
思考这样子的一个问题:“如果在写数据的过程中,可以先删除cache,再更新DB吗?”
答案:答案肯定是不行的,因为这样子可能造成数据库和缓存数据不一致的问题,比如这个时候有一个数据在DB和缓存都为100,请求1需要将这个数据更新写成200,如果先删除换出再更新数据库的话,在请求1已经删除缓存但是数据库还没写完的时候,有一个请求2读取数据,首先去缓存读取,发现缓存被删除了,然后去数据库读取得到100(这个时候请求1还没写完)再写入缓存,这个时候请求1写完了,这个时候数据库里数据为200,缓存里为100,不一致。
可以简单描述为:
请求1先把cache中的数据删除 -> 请求2从DB中读取数据 -> 请求1再把DB中的数据更新
紧接着思考:“在写数据的过程中,如果先写BD,再删除cache就不会造成数据不一致了吗?”
答案:理论上来说还是会出现数据不一致的问题,不过概率很小,因为缓存的写入速度是比数据库写入速度快很多。
比如请求1先读数据A,请求2随后写数据A,并且数据A不在缓存中存在的话就会去数据库读取,读取完请求2再更新完并删除缓存,然后请求1把数据A写入缓存,这个时候数据库和缓存就不一致了。
这个过程可以简单的描述为:
请求1从DB读取数据A -> 请求2写更新数据A到数据库再删除cache中的A数据 -> 请求1将数据A写入缓存
缺点
首次请求的数据一定不在cache的问题
解决办法:可以将热点数据提前写入
cache
中。写操作比较频繁的话导致cache中的数据会被频繁的删除,这样会影响缓存命中率。
解决办法:
- 数据库和缓存强一直场景:更新
DB
的时候同样更新cache
,不过需要加一个锁/分布式锁来保证更新cache
的时候不存在线程安全问题。 - 可以短暂的允许数据库和缓存数据不一致的场景:更新
DB
的时候同样更新cache
,但是给缓存加一个比较短的过期时间,这样的话就可以保证即使数据不一致的话影响也比较小。
- 数据库和缓存强一直场景:更新
三、读写穿透(Read/Write Through Pattern)
读写穿透中服务端把cache
视为主要数据存储,从中读取数据并将数据写入其中。cache
服务负责将此数据读取和写入DB
,从而减轻应用程序的职责。
读写步骤
写:
- 先查
cache
,cache
中不存在,直接更新DB
。 cache
中存在,则先更新cache
,然后cache
服务自己更新DB
(同时更新DB
和cache
)。
如下图:
读:
- 先从
cache
中读取数据,读取到直接返回。 - 从
cache
中读取不到,则先从DB
加载写入到cache
后返回响应。
如下图:
读写穿透实际是在旁路缓存之上进行了封装。在旁路缓存下,发生读请求的时候,如果cache
中不存在对应的数据,是由客户端自己负责把数据写入cache
,而读写穿透则是cache
服务自己来写入缓存,这对客户端是透明的。
和旁路缓存一样,读写穿透也存在首次请求数据一定不在cache
中的问题,对于热点数据可以提前写入缓存中。
四、异步缓存写入(Write Behind Pattern)
异步缓存写入和读写穿透很相似,两者都是由cache
服务来负责cache
和DB
的读写。
两者最大的不同点就是:读写穿透是同步更新DB
和cache
,而异步缓存写入则是只更新cache
,不直接更新DB
,而是改为异步批量的方式更新DB
。
很明显,这种方式对数据一致性带来了更大的挑战,比如cache
数据可能还没异步更新DB
,cache
服务可能就挂了。
这种策略在我们平时开发过程中也非常少见,但是不代表它的应用场景少,比如消息队列中消息的异步写入磁盘、MySQL
的InnoDB Buffer Pool
机制都用到了这种策略。
异步缓存写入的写性能非常高,非常适合写数据经常变化又对数据一致性要求没那么高的场景下使用,比如浏览量、点赞量等。
参考:https://javaguide.cn/database...
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。