ToB企服应用市场:ToB评测及商务社交产业平台

标题: Redis的缓存一致性问题详解 [打印本页]

作者: 科技颠覆者    时间: 2023-3-13 21:41
标题: Redis的缓存一致性问题详解
1、三种常用的缓存模式

1.旁路缓存模式

一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化串到一个内存队列里去。
采用缓存 + 数据库读写的方式,就是 Cache Aside Pattern(旁路缓存模式)。
2.读写穿透模式

Read/Write Through Pattern 中服务端把 cache 视为主要数据存储,从中读取数据并将数据写入其中。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 的问题,对于热点数据可以提前放入缓存中。
3.异步缓存写入

异步缓存写入(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 的写性能非常高,非常适合一些数据经常变化又对数据一致性要求没那么高的场景,比如浏览量、点赞量。
2、缓存存在的问题?

1.为什么先更新后删除?

结论:无论先删除还是先更新数据库都存在数据一致性问题,那么矮个子里选将军,选个发生问题概率小的,就是先更新数据库后删除缓存。
先删除缓存,再更新数据库:如果删除缓存失败了,那么会导致数据库中是新数据,缓存中是旧数据,数据就出现了不一致。
2 个线程要并发「读写」数据,可能会发生以下场景:
最终 X 的值在缓存中是 1(旧值),在数据库中是 2(新值),发生不一致。
先更新数据库,再删除缓存:先删除了缓存,然后要去修改数据库,此时还没修改。一个请求过来,去读缓存,发现缓存空了,去查询数据库,查到了修改前的旧数据,并将其放到了缓存中。随后数据变更的程序完成了数据库的修改。数据库和缓存中的数据不一样了
最终 X 的值在缓存中是 1(旧值),在主从库中是 2(新值),也发生不一致。
这 2 个问题的核心在于:缓存都被回种了「旧值」
矮个子里选将军
第二种方法其实概率很低,这是因为它必须满足 3 个条件:
仔细想一下,条件 3 发生的概率其实是非常低的。
因为写数据库一般会先「加锁」,所以更新数据库,通常是要比读数据库的时间更长的,并且因为缓存的写入速度是比数据库的写入速度快很多。这么来看,「先更新数据库 + 再删除缓存」的方案,是可以保证数据一致性的。所以,我们应该采用这种方案,来操作数据库和缓存。
2.解决方法

最有效的办法就是,把缓存删掉。但是,不能立即删,而是需要「延迟删」,即:缓存延迟双删策略
解决第一个问题:在线程 A 删除缓存、更新完数据库之后,先「休眠一会」,再「删除」一次缓存。
解决第二个问题:线程 A 可以生成一条「延时消息」,写到消息队列中,消费者延时「删除」缓存。
这两个方案的目的,都是为了把缓存清掉,这样一来,下次就可以从数据库读取到最新值,写入缓存。
3.如何保证删除缓存成功?

方案一:重试

首先想到的一个方案是:执行失败后,重试。失败后立即重试的问题在于:
方案二:异步重试

异步重试其实就是:把重试请求扔到「消息队列」中,然后由专门的消费者来重试,直到成功。把重试或第二步操作放到另一个服务中,这个服务用消息队列来进行重试操作。
3、异步重试方案-canal

我们的业务应用在修改数据时,「只需」修改数据库,无需操作缓存。拿 MySQL 举例,当一条数据发生修改时,MySQL 就会产生一条变更日志(Binlog),我们可以订阅这个日志,拿到具体操作的数据,然后再根据这条数据,去删除对应的缓存。订阅变更日志,目前也有了比较成熟的开源中间件,例如阿里的 canal,使用这种方案的优点在于:
想要保证数据库和缓存一致性,推荐采用「先更新数据库,再删除缓存」方案,并配合「消息队列」或「订阅变更日志」的方式来做

参考: https://www.cnblogs.com/myseries/p/12068845.html
3种常用的缓存读写策略详解
缓存和数据库一致性问题,看这篇就够了 - 水滴与银弹

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4