乐趣区

关于java:Offer快到碗里来字节三面缓存与数据库一致性如何保证呢

微信公众号:大黄奔跑
关注我,可理解更多乏味的面试相干问题。

写在之前

Hello,大家好,我是只会写 HelloWorld 的程序员大黄。

本文的主题题目曾经阐明,明天想和大家讨论一下,开发或者面试过程中对于缓存与数据库一致性问题该如何答复呢,这个是我字节三面的时候被问到的问题。

<span style=”color: #5bdaed; font-size:1.3em;”>
如果对数据库和缓存具备强一致性要求时,不要利用缓存了,因而依据 CAP 实践,只有波及到双写就肯定存在一致性问题。咱们明天探讨的前提是对于缓存与数据库没有强统一要求。
</span>

如果能够容忍临时的不统一,通常的做法给缓存设置一个过期工夫,所有的写操作都以数据库为准,缓存过期后从数据库中取值,保留了数据最终一致性,尽可能得升高数据库和缓存之间的不统一。

如果不依赖过期工夫,比方如果要批改某条记录,该如何保障数据库和缓存的一致性呢?

上面说一下常见的三种思路

  1. 先更新数据库、再更新缓存
  2. 先删除缓存、再更新数据库
  3. 先更新数据库、再删除缓存

先更新数据库、再更新缓存

论断:这种策略会存在线程平安及资源损耗问题,不适宜生产环境。

比方同时有两个申请 A 和申请 B,可能存在如下状况:

(1) 申请 A 更新数据库(比方 age=12)
(2) 申请 B 更新数据库(比方 age=22)
(3) 申请 B 更新缓存
(4) 申请 A 更新缓存

这样会产生什么问题?
缓存中存储的数据是谬误的,重大的脏数据(数据库的数据曾经变成了 age=22,然而缓存中 age 还是 12)

为什么会产生这种问题呢?
比方申请 A 比申请 B 先更新数据库,然而因为其余的起因导致(比方网络、过后服务器性能等),申请 B 执行更快,先更新了缓存,之后申请 A 再更新缓存,这就导致了脏数据(数据库和缓存中数据不统一)。

论断中说到会产生资源损耗问题,为什么呢?
(1)如果以后业务属于写多读少,每次对数据库批改之后,都须要再次更新缓存,产生了没有必要的开销。
(2)如果数据库和缓存中存储的数据不是简略的 copy,而是通过简单的运算,频繁的更新缓存,产生的资源损耗同样不容忽视。

先删除缓存、再更新数据库

论断:该计划同样会导致线程平安问题。

同样针对两个申请,比方申请 A(作用将 age=10 改为 age=12)和申请 B(查问 age,原来 age=10),可能存在如下状况:

具体的状况:

(1) 申请 A 删除缓存(缓存中 age=10 被删除掉)
(2) 申请 B 查问信息(发现缓存不存在)
(3) 申请 B 缓存穿透,查询数据库(失去了 age=10)
(4) 回填到缓存中(将 age=10 回填到缓存中)
(4) 申请 A 更新数据库(将数据库中 age=10 改为 age=12)

发现没有,数据库存储的 age=12,然而缓存中存储的 age=10,数据库和缓存间接不统一,缓存中存储的依然是旧值,数据库中存储的是脏数据。

下面的数据库临时还不思考主从备份的状况下,如果是主库写、从库读,则脏数据可能性更高。
比方上面是两个申请:

(1) 申请 A 删除缓存(缓存中 age=10 被删除掉)
(2) 更新数据库,写入主库
(3) 申请 B 查问信息(发现缓存不存在)
(4) 申请 B 缓存穿透,查询数据库(失去了 age=10)
(5) 回填到缓存中(将 age=10 回填到缓存中)
(6) 将主库中的数据同步到从库中(age=12)

同样会导致数据库和缓存之间的不统一。

先更新数据库、再删除缓存

论断:与后面两种办法不同,该办法先更新数据库,而后间接删除缓存,这种办法能够吗?我感觉同样会存在线程平安问题,只是概率会比拟小而已。

比方如下场景,假如申请 A(查问数据)和申请 B(更新数据,将 age=10 更新为 age=12)

下面图中能够看出,申请 A 为读取到了旧数据并且刚好将旧数据回填到缓存中。

这种状况的概率实际上比前两种低的,申请 A 来的时候,缓存刚好生效,而且还在读申请(申请 A)比写申请慢(申请 B),个别状况下,数据库的读取操作比写操作更快的,通常申请 A 的 2、3 步比申请 B 的 4、5 步更放慢,所以这种状况下,产生数据库和缓存不统一的概率更加低。

还有什么更加优化的办法吗?

如果想要数据库和缓存之间不统一概率和工夫更低,能够采纳如下思路:

  1. 给缓存设置过期工夫。缓存过期后间接取数据库的值,进一步升高不统一工夫。
  2. 采纳缓存延时双删策略。在更新数据库,删除缓存之后,过一段时间再删除缓存。
  3. 保障的重试策略。

缓存延时双删该如何做呢?

还是以申请 A(更新数据)、申请 B(查问数据)为例

当然这种也不能齐全杜绝缓存和数据库之间的不统一问题,因为无奈保障申请 B 的第二次删除肯定在申请 A 的回填之后实现。当然这种概率多大呢?

如果还是不能够容忍这么低的概率能够采纳重试策略。
具体思路有两种:

通过音讯队列自发自收的办法进行双重删除

具体的业务流程图

该办法的外围将须要再次删除的 key 间接扔进音讯队列中,音讯队列自身为异步的,能够完满的做到延时性能。
然而该办法有一个弊病,须要写发送音讯和接管音讯的办法,并且须要将这套逻辑耦合到原业务代码中,算是对原业务造成了侵入。

通过监听数据库的 binlog 来进行双删

总结

本文次要想和大家探讨一个陈词滥调的问题,缓存与数据库一致性。探讨本文的前提是业务场景不要求缓存与数据之间的强一致性。比方订单领取问题不倡议应用缓存,如果有人面试中不分青红皂白的说如何保障缓存与数据库的强一致性,大家能够让他 gun 了。

最初大黄分享多年面试心得。面试中,面对一个问题,大略依照总分的逻辑答复即可。先间接抛出论断,而后举例论证本人的论断。肯定要第一工夫抓住面试官的心里,否则容易给人抓不着重点或者不着边际的印象。

番外

另外,关注大黄奔跑公众号,第一工夫播种独家整顿的面试实战记录及面试知识点总结。

我是大黄,一个只会写 HelloWorld 的程序员,咱们下期见。

退出移动版