自拍偷在线精品自拍偷,亚洲欧美中文日韩v在线观看不卡

八種方案,保證緩存和數(shù)據(jù)庫(kù)的最終一致性

數(shù)據(jù)庫(kù) 其他數(shù)據(jù)庫(kù)
我們通常使用緩存機(jī)制來提升系統(tǒng)的性能,緩存系統(tǒng)下的讀寫操作,一般都需要操作數(shù)據(jù)庫(kù)與緩存。對(duì)于讀操作,一般是先查詢緩存,查詢不到再查詢數(shù)據(jù)庫(kù),最后回寫進(jìn)緩存。

[[437738]]

前言

我們通常使用緩存機(jī)制來提升系統(tǒng)的性能,緩存系統(tǒng)下的讀寫操作,一般都需要操作數(shù)據(jù)庫(kù)與緩存。

對(duì)于讀操作,一般是先查詢緩存,查詢不到再查詢數(shù)據(jù)庫(kù),最后回寫進(jìn)緩存。

而對(duì)于寫操作,究竟是先刪除(更新)緩存,再更新數(shù)據(jù)庫(kù),還是先更新數(shù)據(jù)庫(kù),再刪除(更新)緩存呢?

由于對(duì)數(shù)據(jù)庫(kù)以及緩存的整體操作,并不是原子性的,再加上讀寫并發(fā),究竟什么樣的方案可以保證數(shù)據(jù)庫(kù)與緩存的一致性呢?

下面介紹8種方案,配合讀寫時(shí)序圖,希望你能從其中了解到保證一致性的設(shè)計(jì)要點(diǎn)。

方案1 給緩存設(shè)置過期時(shí)間

這種方案適用于對(duì)數(shù)據(jù)一致性要求較低或者寫請(qǐng)求很少的業(yè)務(wù),當(dāng)讀請(qǐng)求沒有命中緩存時(shí),就從數(shù)據(jù)庫(kù)中讀,之后回寫到緩存里,同時(shí)設(shè)置一個(gè)過期時(shí)間。

寫請(qǐng)求直接更改數(shù)據(jù)庫(kù),不用操作緩存。因此當(dāng)一個(gè)key沒過期時(shí),寫請(qǐng)求更改了數(shù)據(jù)庫(kù),之后的讀還是讀取到舊數(shù)據(jù)。這個(gè)時(shí)候確實(shí)發(fā)生了不一致,但業(yè)務(wù)并不敏感。

方案2 先更新數(shù)據(jù)庫(kù),再更新緩存

如果利用到緩存,那么肯定是讀多寫少的場(chǎng)景。但不能否定的是,可能會(huì)存在突發(fā)的寫多讀少的階段。

在這個(gè)特殊的階段中,會(huì)頻繁地更改數(shù)據(jù)庫(kù)與緩存,但緩存不會(huì)被頻繁地讀,更新緩存是在做無用功。

該方案可能還會(huì)有將臟數(shù)據(jù)寫回到緩存中的風(fēng)險(xiǎn):

​當(dāng)再有讀請(qǐng)求過來時(shí),會(huì)直接從緩存中查詢到1,而數(shù)據(jù)庫(kù)中的值為3,造成不一致。

因此,該方案的不足在于:

  • 寫多讀少時(shí),頻繁更新緩存會(huì)降低性能
  • 并發(fā)情況下可能存在將臟數(shù)據(jù)寫回緩存的風(fēng)險(xiǎn)

方案3 先更新緩存,再更新數(shù)據(jù)庫(kù)

和方案2類似,也會(huì)存在相同的問題。

方案4 先更新數(shù)據(jù)庫(kù),再刪除緩存

既然方案2與方案3都是更新緩存,這里不妨直接刪除緩存呢?

​當(dāng)讀寫串行時(shí),不會(huì)發(fā)生不一致的情況,貌似是一種比較好的方案。

不過看一下這個(gè)例子:

​首先系統(tǒng)處于一個(gè)緩存過期的初始狀態(tài),接著讀寫并發(fā)。由于讀請(qǐng)求讀到了數(shù)據(jù)庫(kù)的舊值,而由于某種原因,回寫發(fā)生在寫請(qǐng)求執(zhí)行完畢之后,造成了刷臟的問題。

這種問題發(fā)生的概率較低,首先緩存得過期,再者讀請(qǐng)求的整條鏈路的執(zhí)行速度慢于寫請(qǐng)求。一般來說,讀肯定是快于寫的。

方案5 先刪除緩存,再更新數(shù)據(jù)庫(kù)

同樣,當(dāng)存在讀寫并發(fā)時(shí),事情就不會(huì)往預(yù)料的方向上發(fā)展了,看下面這個(gè)例子:

​寫請(qǐng)求刪除緩存后,讀請(qǐng)求無法命中緩存,因此讀到數(shù)據(jù)庫(kù)的舊值2。寫請(qǐng)求更新完數(shù)據(jù)庫(kù)后,讀請(qǐng)求再將1回寫進(jìn)緩存,同樣存在刷臟的風(fēng)險(xiǎn)。

如果a永不過期的且后續(xù)沒有執(zhí)行寫請(qǐng)求的話,那么讀到的一直都是臟數(shù)據(jù),因此我們一般都會(huì)設(shè)置緩存的過期時(shí)間,作為一種兜底策略。在a過期后,就會(huì)重新從數(shù)據(jù)庫(kù)中讀取。

該問題發(fā)生的概率一般會(huì)高于方案4,那如何去解決呢?

可不可以主動(dòng)讓臟數(shù)據(jù)過期,也就是讓寫請(qǐng)求再刪一次緩存呢?

可以的,這種方案稱作為延時(shí)雙刪。

方案6 延時(shí)雙刪

在方案5的第2個(gè)案例圖上進(jìn)行修改:在讀請(qǐng)求刷臟后,寫請(qǐng)求再次刪除緩存。

​此方案的難點(diǎn)在于,sleep的時(shí)間該怎么去確定。如果偏大,同步刪除的話會(huì)造成吞吐量的降低與查臟。如果偏小,則有可能第二次刪除在刷臟之前發(fā)生,起不到“雙刪”的作用。

因此,我們需要結(jié)合業(yè)務(wù)對(duì)sleep的時(shí)間做出評(píng)估。一般來說,sleep的時(shí)間應(yīng)該稍大于讀請(qǐng)求查詢數(shù)據(jù)與回寫緩存的時(shí)間。

延時(shí)雙刪,對(duì)使用讀寫分離,主從同步的數(shù)據(jù)庫(kù)也有奇效。

在主從同步正常且沒有出現(xiàn)讀寫并發(fā)的情況下,數(shù)據(jù)庫(kù)與緩存是一致的

 

 

如果主從同步存在延遲呢?導(dǎo)致讀請(qǐng)求讀到a=2,最終會(huì)造成不一致的情況

​如果使用延時(shí)雙刪,就可以有效解決

​不過這里的sleep時(shí)間=讀請(qǐng)求的查詢從庫(kù)時(shí)間+回寫緩存時(shí)間+主從同步的延遲時(shí)間

不過為了規(guī)避主從同步延遲造成的數(shù)據(jù)庫(kù)與緩存的不一致,可以強(qiáng)迫寫之后的快速讀走主庫(kù)。

不過這里還是希望大家,多去了解可能造成主從同步延遲的原因,例如從庫(kù)性能差,本地重放sql進(jìn)度慢;從庫(kù)數(shù)量少,造成大量讀之下占用全部cpu;從庫(kù)是否正在執(zhí)行DDL語(yǔ)句或者慢查詢等。

延時(shí)雙刪看起來趨于完美了,但較真的同學(xué)始終不認(rèn)賬。

  • 延時(shí)是使用同步的延時(shí),造成吞吐量降低怎么辦?
  • 雙刪中第二次刪除怎么辦?

對(duì)于第一個(gè)問題,可以將第二次刪除改為異步的。

對(duì)于第二個(gè)問題,可以將第二次刪除改為可重試的。

其實(shí)第二個(gè)問題,也存在于方案4中,即先更新數(shù)據(jù)庫(kù),再刪除緩存。

我們拿方案4進(jìn)行優(yōu)化,可以引入消息中間件。

方案7 消息隊(duì)列

先更新數(shù)據(jù)庫(kù),接著將刪除緩存的消息投遞到mq中。自身拿到消息后,嘗試進(jìn)行刪除緩存。如果失敗,則不斷進(jìn)行重試。

​引入了消息隊(duì)列,系統(tǒng)的復(fù)雜性提升,可用性降低。

也會(huì)帶來各種各樣的問題,例如消息丟失、亂序與重復(fù)消費(fèi)等。亂序與重復(fù)消費(fèi)的問題,在刪除緩存的場(chǎng)景下,不會(huì)造成任何問題。

不過如果一條刪除緩存的消息的丟失,將會(huì)導(dǎo)致在緩存過期前出現(xiàn)數(shù)據(jù)不一致的情況。

這里稍微帶一下mq中如何保證消息不丟失的措施:需要生產(chǎn)端、mq自身與消費(fèi)端共同去保障。

  • 生產(chǎn)端,對(duì)生產(chǎn)的消息進(jìn)行狀態(tài)標(biāo)記,開啟confirm機(jī)制,依據(jù)mq的響應(yīng)來更新消息狀態(tài),使用定時(shí)任務(wù)重新投遞超時(shí)的消息,多次投遞失敗進(jìn)行報(bào)警。
  • mq自身,開啟持久化,并在落盤后再進(jìn)行ack。如果是鏡像部署模式,需要在同步到多個(gè)副本之后再進(jìn)行ack。
  • 消費(fèi)端,開啟手動(dòng)ack模式,在業(yè)務(wù)處理完成后再進(jìn)行ack,并且需要保證冪等。

通過以上的處理,理論上不存在消息丟失的情況,但是系統(tǒng)的吞吐量以及性能有所下降。

如果想要詳細(xì)了解如何在各個(gè)階段保證消息不丟失,可以移步我的另外一篇文章RabbitMQ如何在各個(gè)環(huán)節(jié)保證消息不丟失

引入消息隊(duì)列,帶來了可以異步重試的好處,但同時(shí)需要通過多種機(jī)制去保證刪除消息不丟失。此外,該方案會(huì)對(duì)業(yè)務(wù)代碼造成一定的侵入。

方案8 消息隊(duì)列+訂閱binlog

業(yè)務(wù)代碼只操作數(shù)據(jù)庫(kù),不操作緩存。同時(shí)啟動(dòng)一個(gè)訂閱binlog的程序去監(jiān)聽刪除操作,然后投遞到消息隊(duì)列中。再啟動(dòng)一個(gè)消費(fèi)者,根據(jù)消息去刪除緩存。

對(duì)binlog不熟悉的同學(xué),可以參考我的另外一篇文章數(shù)據(jù)庫(kù)日志——binlog、redo log、undo log掃盲

​在MySQL中,可以使用canal中間件來訂閱binlog。

在該方案中,再次使用一個(gè)中間件來幫我們完成解耦工作,但系統(tǒng)的復(fù)雜度確實(shí)也在逐步上升。

總結(jié)

給緩存設(shè)置過期時(shí)間

簡(jiǎn)單直接,適用于對(duì)數(shù)據(jù)一致性要求較低或者寫請(qǐng)求很少的業(yè)務(wù)

先更新數(shù)據(jù)庫(kù),再更新緩存

先更新緩存,再更新數(shù)據(jù)庫(kù)

  • 寫多讀少時(shí),頻繁更新緩存會(huì)降低性能
  • 并發(fā)情況下可能存在將臟數(shù)據(jù)寫回緩存的風(fēng)險(xiǎn)

先更新數(shù)據(jù)庫(kù),再刪除緩存

  • 極低概率在讀寫并發(fā)時(shí)發(fā)生刷臟

先刪除緩存,再更新數(shù)據(jù)庫(kù)

  • 較低概率在讀寫并發(fā)時(shí)發(fā)生刷臟

延時(shí)雙刪

  • sleep的由業(yè)務(wù)評(píng)估,稍大于讀請(qǐng)求的查詢數(shù)據(jù)庫(kù)與回寫緩存的時(shí)間
  • 對(duì)主從同步延遲也有奇效
  • 存在第二次刪除失敗的情況

消息隊(duì)列

  • 對(duì)刪除失敗的消息進(jìn)行異步重試
  • 會(huì)對(duì)業(yè)務(wù)代碼造成一定的侵入

消息隊(duì)列+訂閱binlog

  • 解耦
  • 系統(tǒng)復(fù)雜度上升

最后

以上的所有方案,都是盡可能的保證數(shù)據(jù)庫(kù)與緩存的一致性,也就是最終一致性。

如果使用CAP理論來看待這個(gè)由業(yè)務(wù)代碼+數(shù)據(jù)庫(kù)+緩存組成的分布式系統(tǒng),首先該系統(tǒng)必須要能容忍網(wǎng)絡(luò)分區(qū),其次對(duì)于覺得部分的場(chǎng)景,該分布式系統(tǒng)應(yīng)當(dāng)也需要滿足可用性。也就是說,緩存節(jié)點(diǎn)宕機(jī)后或出現(xiàn)網(wǎng)絡(luò)閃斷,整個(gè)系統(tǒng)應(yīng)當(dāng)還能夠?qū)ν馓峁┓?wù)。根據(jù)CAP定理,該系統(tǒng)就無法滿足強(qiáng)一致性。對(duì)CAP不熟悉的同學(xué),可以參考我的另外一篇文章常說的分布式系統(tǒng)核心理論CAP與BASE到底是什么

如果就要保證強(qiáng)一致性,例如使用Raft方案來做強(qiáng)一致。如果能做到強(qiáng)一致,那么整個(gè)系統(tǒng)的性能就會(huì)大打折扣。使用到緩存,就會(huì)為了提升性能。因此,強(qiáng)一致一般與提升性能是背道而馳的。當(dāng)然,緩存是有過期時(shí)間的,這種兜底操作將徹底避免永遠(yuǎn)出現(xiàn)不一致的情況。

對(duì)分布式一致性算法Raft不了解的同學(xué),可以參考我的另外一篇文章22張圖,帶你入門分布式一致性算法Raft

從方案1到方案8,系統(tǒng)的復(fù)雜性逐步上升,但確實(shí)能解決一些痛點(diǎn),例如同步刪除性能差,第二次刪除失敗等等。

但是并不存在誰(shuí)最好誰(shuí)最差,應(yīng)當(dāng)結(jié)合業(yè)務(wù)來看,脫離業(yè)務(wù)談技術(shù)就是一場(chǎng)空談。

作為技術(shù)人員,我們應(yīng)當(dāng)根據(jù)業(yè)務(wù)場(chǎng)景選擇相應(yīng)的技術(shù),但前提是對(duì)各種技術(shù)都有較深的理解,能分析其利弊。

我覺得技術(shù)人員的最好的歸宿,就是能在不斷解決問題的過程中,形成自己的方法論與解決方案。例如形成開源作品或技術(shù)博客,去影響別人。

 

責(zé)任編輯:姜華 來源: 今日頭條
相關(guān)推薦

2024-10-28 12:41:25

2022-03-29 10:39:10

緩存數(shù)據(jù)庫(kù)數(shù)據(jù)

2019-09-20 21:50:47

數(shù)據(jù)庫(kù)緩存

2019-09-04 08:13:31

數(shù)據(jù)庫(kù)緩存存儲(chǔ)

2022-03-31 08:21:14

數(shù)據(jù)庫(kù)緩存雙寫數(shù)據(jù)一致性

2022-04-01 16:55:22

數(shù)據(jù)庫(kù)緩存日志

2020-09-03 09:45:38

緩存數(shù)據(jù)庫(kù)分布式

2022-12-05 08:24:32

mongodb數(shù)據(jù)庫(kù)數(shù)據(jù)

2024-12-26 15:01:29

2020-06-01 22:09:48

緩存緩存同步緩存誤用

2021-07-26 06:33:42

CRDT數(shù)據(jù)CAP

2021-06-11 09:21:58

緩存數(shù)據(jù)庫(kù)Redis

2025-04-27 08:52:21

Redis數(shù)據(jù)庫(kù)緩存

2018-09-11 10:46:10

緩存數(shù)據(jù)庫(kù)一致性

2022-02-23 09:17:09

數(shù)據(jù)庫(kù)分離變更

2022-12-14 08:23:30

2025-03-05 09:10:00

session開發(fā)Web

2025-02-10 03:00:00

2021-09-08 11:03:13

緩存數(shù)據(jù)庫(kù)性能

2019-08-30 12:46:10

并發(fā)扣款查詢SQL
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號(hào)