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

掘地三尺搞定 Redis 與 MySQL 數(shù)據(jù)一致性問題

數(shù)據(jù)庫 Redis MySQL
今天「碼哥」跟大家一起深入探索緩存的工作機(jī)制和緩存一致性應(yīng)對方案。

?Redis 擁有高性能的數(shù)據(jù)讀寫功能,被我們廣泛用在緩存場景,一是能提高業(yè)務(wù)系統(tǒng)的性能,二是為數(shù)據(jù)庫抵擋了高并發(fā)的流量請求,??點我 -> 解密 Redis 為什么這么快的秘密??。

把 Redis 作為緩存組件,需要防止出現(xiàn)以下的一些問題,否則可能會造成生產(chǎn)事故。

今天「碼哥」跟大家一起深入探索緩存的工作機(jī)制和緩存一致性應(yīng)對方案。

在本文正式開始之前,我覺得我們需要先取得以下兩點的共識:

  1. 緩存必須要有過期時間;
  2. 保證數(shù)據(jù)庫跟緩存的最終一致性即可,不必追求強(qiáng)一致性。

一、什么是數(shù)據(jù)庫與緩存一致性

數(shù)據(jù)一致性指的是:

  • 緩存中存有數(shù)據(jù),緩存的數(shù)據(jù)值 = 數(shù)據(jù)庫中的值。
  • 緩存中沒有該數(shù)據(jù),數(shù)據(jù)庫中的值 = 最新值。

反推緩存與數(shù)據(jù)庫不一致:

  • 緩存的數(shù)據(jù)值 ≠ 數(shù)據(jù)庫中的值。
  • 緩存或者數(shù)據(jù)庫存在舊的數(shù)據(jù),導(dǎo)致線程讀取到舊數(shù)據(jù)。

為何會出現(xiàn)數(shù)據(jù)一致性問題呢?

把 Redis 作為緩存的時候,當(dāng)數(shù)據(jù)發(fā)生改變我們需要雙寫來保證緩存與數(shù)據(jù)庫的數(shù)據(jù)一致。

數(shù)據(jù)庫跟緩存,畢竟是兩套系統(tǒng),如果要保證強(qiáng)一致性,勢必要引入 ??2PC??? 或 ??Paxos?? 等分布式一致性協(xié)議,或者分布式鎖等等,這個在實現(xiàn)上是有難度的,而且一定會對性能有影響。

如果真的對數(shù)據(jù)的一致性要求這么高,那引入緩存是否真的有必要呢?

二、緩存的使用策略

在使用緩存時,通常有以下幾種緩存使用策略用于提升系統(tǒng)性能:

  • ??Cache-Aside Pattern??(旁路緩存,業(yè)務(wù)系統(tǒng)常用)
  • ??Read-Through Pattern??
  • ??Write-Through Pattern??
  • ??Write-Behind Pattern??

1、Cache-Aside (旁路緩存)

所謂「旁路緩存」,就是讀取緩存、讀取數(shù)據(jù)庫和更新緩存的操作都在應(yīng)用系統(tǒng)來完成,業(yè)務(wù)系統(tǒng)最常用的緩存策略。

(1)讀取數(shù)據(jù)

圖片

讀取數(shù)據(jù)邏輯如下:

  1. 當(dāng)應(yīng)用程序需要從數(shù)據(jù)庫讀取數(shù)據(jù)時,先檢查緩存數(shù)據(jù)是否命中。
  2. 如果緩存未命中,則查詢數(shù)據(jù)庫獲取數(shù)據(jù),同時將數(shù)據(jù)寫到緩存中,以便后續(xù)讀取相同數(shù)據(jù)會命中緩存,最后再把數(shù)據(jù)返回給調(diào)用者。
  3. 如果緩存命中,直接返回。

時序圖如下:

圖片

圖片旁路緩存讀時序圖

優(yōu)點

  • 緩存中僅包含應(yīng)用程序?qū)嶋H請求的數(shù)據(jù),有助于保持緩存大小的成本效益。
  • 實現(xiàn)簡單,并且能獲得性能提升。

實現(xiàn)的偽代碼如下:

String cacheKey = "公眾號:碼哥字節(jié)";
String cacheValue = redisCache.get(cacheKey);
//緩存命中
if (cacheValue != null) {
return cacheValue;
} else {
//緩存缺失, 從數(shù)據(jù)庫獲取數(shù)據(jù)
cacheValue = getDataFromDB();
// 將數(shù)據(jù)寫到緩存中
redisCache.put(cacheValue)
}

缺點

由于數(shù)據(jù)僅在緩存未命中后才加載到緩存中,因此初次調(diào)用的數(shù)據(jù)請求響應(yīng)時間會增加一些開銷,因為需要額外的緩存填充和數(shù)據(jù)庫查詢耗時。

(2)更新數(shù)據(jù)

使用 ??cache-aside?? 模式寫數(shù)據(jù)時,如下流程。

圖片

旁路緩存寫數(shù)據(jù)

  1. 寫數(shù)據(jù)到數(shù)據(jù)庫;
  2. 將緩存中的數(shù)據(jù)失效或者更新緩存數(shù)據(jù);

使用 ??cache-aside?? 時,最常見的寫入策略是直接將數(shù)據(jù)寫入數(shù)據(jù)庫,但是緩存可能會與數(shù)據(jù)庫不一致。

我們應(yīng)該給緩存設(shè)置一個過期時間,這個是保證最終一致性的解決方案。

如果過期時間太短,應(yīng)用程序會不斷地從數(shù)據(jù)庫中查詢數(shù)據(jù)。同樣,如果過期時間過長,并且更新時沒有使緩存失效,緩存的數(shù)據(jù)很可能是臟數(shù)據(jù)。

最常用的方式是刪除緩存使緩存數(shù)據(jù)失效。

為啥不是更新緩存呢?

性能問題

當(dāng)緩存的更新成本很高,需要訪問多張表聯(lián)合計算,建議直接刪除緩存,而不是更新緩存數(shù)據(jù)來保證一致性。

安全問題

在高并發(fā)場景下,可能會造成查詢查到的數(shù)據(jù)是舊值,具體待會碼哥會分析,大家別急。

2、Read-Through(直讀)

當(dāng)緩存未命中,也是從數(shù)據(jù)庫加載數(shù)據(jù),同時寫到緩存中并返回給應(yīng)用系統(tǒng)。

雖然 ??read-through??? 和 ??cache-aside??? 非常相似,在 ??cache-aside?? 中應(yīng)用系統(tǒng)負(fù)責(zé)從數(shù)據(jù)庫獲取數(shù)據(jù)和填充緩存。

而 Read-Through 將獲取數(shù)據(jù)存儲中的值的責(zé)任轉(zhuǎn)移到了緩存提供者身上。

圖片

Read-Through

Read-Through 實現(xiàn)了關(guān)注點分離原則。代碼只與緩存交互,由緩存組件來管理自身與數(shù)據(jù)庫之間的數(shù)據(jù)同步。

3、Write-Through 同步直寫

與 Read-Through 類似,發(fā)生寫請求時,Write-Through 將寫入責(zé)任轉(zhuǎn)移到緩存系統(tǒng),由緩存抽象層來完成緩存數(shù)據(jù)和數(shù)據(jù)庫數(shù)據(jù)的更新,時序流程圖如下:

圖片

Write-Through

??Write-Through?? 的主要好處是應(yīng)用系統(tǒng)的不需要考慮故障處理和重試邏輯,交給緩存抽象層來管理實現(xiàn)。

優(yōu)缺點

單獨直接使用該策略是沒啥意義的,因為該策略要先寫緩存,再寫數(shù)據(jù)庫,對寫入操作帶來了額外延遲。

當(dāng)??Write-Through??? 與 ??Read-Through??? 配合使用,就能成分發(fā)揮 ??Read-Through?? 的優(yōu)勢,同時還能保證數(shù)據(jù)一致性,不需要考慮如何將緩存設(shè)置失效。

圖片Write-Through

這個策略顛倒了 ??Cache-Aside?? 填充緩存的順序,并不是在緩存未命中后延遲加載到緩存,而是在數(shù)據(jù)先寫緩存,接著由緩存組件將數(shù)據(jù)寫到數(shù)據(jù)庫。

優(yōu)點

  • 緩存與數(shù)據(jù)庫數(shù)據(jù)總是最新的;
  • 查詢性能最佳,因為要查詢的數(shù)據(jù)有可能已經(jīng)被寫到緩存中了。

缺點

不經(jīng)常請求的數(shù)據(jù)也會寫入緩存,從而導(dǎo)致緩存更大、成本更高。

4、Write-Behind

這個圖一眼看去似乎與 ??Write-Through?? 一樣,其實不是的,區(qū)別在于最后一個箭頭的箭頭:它從實心變?yōu)榫€。

這意味著緩存系統(tǒng)將異步更新數(shù)據(jù)庫數(shù)據(jù),應(yīng)用系統(tǒng)只與緩存系統(tǒng)交互。

應(yīng)用程序不必等待數(shù)據(jù)庫更新完成,從而提高應(yīng)用程序性能,因為對數(shù)據(jù)庫的更新是最慢的操作。

圖片

Write-Behind

這種策略下,緩存與數(shù)據(jù)庫的一致性不強(qiáng),對一致性高的系統(tǒng)不建議使用。

三、旁路緩存下的一致性問題分析

業(yè)務(wù)場景用的最多的就是 ??Cache-Aside?? (旁路緩存) 策略,在該策略下,客戶端對數(shù)據(jù)的讀取流程是先讀取緩存,如果命中則返回;未命中,則從數(shù)據(jù)庫讀取并把數(shù)據(jù)寫到緩存中,所以讀操作不會導(dǎo)致緩存與數(shù)據(jù)庫的不一致。

重點是寫操作,數(shù)據(jù)庫和緩存都需要修改,而兩者就會存在一個先后順序,可能會導(dǎo)致數(shù)據(jù)不再一致。針對寫,我們需要考慮兩個問題:

  • 先更新緩存還是更新數(shù)據(jù)庫?
  • 當(dāng)數(shù)據(jù)發(fā)生變化時,選擇修改緩存(update),還是刪除緩存(delete)?

將這兩個問題排列組合,會出現(xiàn)四種方案:

  1. 先更新緩存,再更新數(shù)據(jù)庫;
  2. 先更新數(shù)據(jù)庫,再更新緩存;
  3. 先刪除緩存,再更新數(shù)據(jù)庫;
  4. 先更新數(shù)據(jù)庫,再刪除緩存。

接下來的分析大家不必死記硬背,關(guān)鍵在于在推演的過程中大家只需要考慮以下兩個場景會不會帶來嚴(yán)重問題即可:

  • 其中第一個操作成功,第二個失敗會導(dǎo)致什么問題?
  • 在高并發(fā)情況下會不會造成讀取數(shù)據(jù)不一致?

為啥不考慮第一個失敗,第二個成功的情況呀?

你猜?

既然第一個都失敗了,第二個就不用執(zhí)行了,直接在第一步返回 50x 等異常信息即可,不會出現(xiàn)不一致問題。

只有第一個成功,第二個失敗才讓人頭痛,想要保證他們的原子性,就涉及到分布式事務(wù)的范疇了。

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

圖片先更新緩存再更新數(shù)據(jù)庫

如果先更新緩存成功,寫數(shù)據(jù)庫失敗,就會導(dǎo)致緩存是最新數(shù)據(jù),數(shù)據(jù)庫是舊數(shù)據(jù),那緩存就是臟數(shù)據(jù)了。

之后,其他查詢立馬請求進(jìn)來的時候就會獲取這個數(shù)據(jù),而這個數(shù)據(jù)數(shù)據(jù)庫中卻不存在。

數(shù)據(jù)庫都不存在的數(shù)據(jù),緩存并返回客戶端就毫無意義了。

該方案直接 ???Pass??。

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

一切正常的情況如下:

  • 先寫數(shù)據(jù)庫,成功;
  • 再 update 緩存,成功。

更新緩存失敗

這時候我們來推斷下,假如這兩個操作的原子性被破壞:第一步成功,第二步失敗會導(dǎo)致什么問題?

會導(dǎo)致數(shù)據(jù)庫是最新數(shù)據(jù),緩存是舊數(shù)據(jù),出現(xiàn)一致性問題。

該圖我就不畫了,與上一個圖類似,對調(diào)下 Redis 和 MySQL 的位置即可。

高并發(fā)場景

謝霸歌經(jīng)常 996,腰酸脖子疼,bug 越寫越多,想去按摩推拿放提升下編程技巧。

疫情影響,單子來之不易,高端會所的技師都爭先恐后想接這一單,高并發(fā)啊兄弟們。

在進(jìn)店以后,前臺會將顧客信息錄入系統(tǒng),執(zhí)行 ??set xx的服務(wù)技師 = 待定??的初始值表示目前無人接待保存到數(shù)據(jù)庫和緩存中,之后再安排技師按摩服務(wù)。

如下圖所示:

圖片

高并發(fā)先更新數(shù)據(jù)庫,再更新緩存

  1. 98 號技師先下手為強(qiáng),向系統(tǒng)發(fā)送??set 謝霸歌的服務(wù)技師 = 98?? 的指令寫入數(shù)據(jù)庫,這時候系統(tǒng)的網(wǎng)絡(luò)出現(xiàn)波動,卡頓了,數(shù)據(jù)還沒來得及寫到緩存。
  2. 接下來,520 號技師也向系統(tǒng)發(fā)送??set 謝霸哥的服務(wù)技師 = 520??寫到數(shù)據(jù)庫中,并且也把這個數(shù)據(jù)寫到緩存中了。
  3. 這時候之前的 98 號技師的寫緩存請求開始執(zhí)行,順利將數(shù)據(jù)??set 謝霸歌的服務(wù)技師 = 98?? 寫到緩存中。

最后發(fā)現(xiàn),數(shù)據(jù)庫的值 = ??set 謝霸哥的服務(wù)技師 = 520???,而緩存的值= ??set 謝霸歌的服務(wù)技師 = 98??。

520 號技師在緩存中的最新數(shù)據(jù)被 98 號技師的舊數(shù)據(jù)覆蓋了。

所以,在高并發(fā)的場景中,多線程同時寫數(shù)據(jù)再寫緩存,就會出現(xiàn)緩存是舊值,數(shù)據(jù)庫是最新值的不一致情況。

該方案直接 pass。

如果第一步就失敗,直接返回 50x 異常,并不會出現(xiàn)數(shù)據(jù)不一致。

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

按照「碼哥」前面說的套路,假設(shè)第一個操作成功,第二個操作失敗推斷下會發(fā)生什么?高并發(fā)場景下又會發(fā)生什么?

第二步寫數(shù)據(jù)庫失敗

假設(shè)現(xiàn)在有兩個請求:寫請求 A,讀請求 B。

寫請求 A 第一步先刪除緩存成功,寫數(shù)據(jù)到數(shù)據(jù)庫失敗,就會導(dǎo)致該次寫數(shù)據(jù)丟失,數(shù)據(jù)庫保存的是舊值。

接著另一個讀請 B 求進(jìn)來,發(fā)現(xiàn)緩存不存在,從數(shù)據(jù)庫讀取舊數(shù)據(jù)并寫到緩存中。

高并發(fā)下的問題

圖片

先刪緩存,再寫數(shù)據(jù)庫

  1. 還是 98 號技師先下手為強(qiáng),系統(tǒng)接收請求把緩存數(shù)據(jù)刪除,當(dāng)系統(tǒng)準(zhǔn)備將??set 肖菜雞的服務(wù)技師 = 98??寫到數(shù)據(jù)庫的時候發(fā)生卡頓,來不及寫入。
  2. 這時候,大堂經(jīng)理向系統(tǒng)執(zhí)行讀請求,查下肖菜雞有沒有技師接待,方便安排技師服務(wù),系統(tǒng)發(fā)現(xiàn)緩存中沒數(shù)據(jù),于是乎就從數(shù)據(jù)庫讀取到舊數(shù)據(jù)??set 肖菜雞的服務(wù)技師 = 待定??,并寫到緩存中。
  3. 這時候,原先卡頓的 98 號技師寫數(shù)據(jù)??set 肖菜雞的服務(wù)技師 = 98??到數(shù)據(jù)庫的操作完成。

這樣子會出現(xiàn)緩存的是舊數(shù)據(jù),在緩存過期之前無法讀取到最數(shù)據(jù)。肖菜雞本就被 98 號技師接單了,但是大堂經(jīng)理卻以為沒人接待。

該方案 pass,因為第一步成功,第二步失敗,會造成數(shù)據(jù)庫是舊數(shù)據(jù),緩存中沒數(shù)據(jù)繼續(xù)從數(shù)據(jù)庫讀取舊值寫入緩存,造成數(shù)據(jù)不一致,還會多一次 cahche。

不論是異常情況還是高并發(fā)場景,會導(dǎo)致數(shù)據(jù)不一致。miss。

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

經(jīng)過前面的三個方案,全都被 pass 了,分析下最后的方案到底行不行。

按照「套路」,分別判斷異常和高并發(fā)會造成什么問題。

該策略可以知道,在寫數(shù)據(jù)庫階段失敗的話就直返返回客戶端異常,不需要執(zhí)行緩存操作了。

所以第一步失敗不會出現(xiàn)數(shù)據(jù)不一致的情況。

刪緩存失敗

重點在于第一步寫最新數(shù)據(jù)到數(shù)據(jù)庫成功,刪除緩存失敗怎么辦?

可以把這兩個操作放在一個事務(wù)中,當(dāng)緩存刪除失敗,那就把寫數(shù)據(jù)庫回滾。

高并發(fā)場景下不合適,容易出現(xiàn)大事務(wù),造成死鎖問題。

如果不回滾,那就出現(xiàn)數(shù)據(jù)庫是新數(shù)據(jù),緩存還是舊數(shù)據(jù),數(shù)據(jù)不一致了,咋辦?

所以,我們要想辦法讓緩存刪除成功,不然只能等到有效期失效那可不行。

使用重試機(jī)制。

比如重試三次,三次都失敗則記錄日志到數(shù)據(jù)庫,使用分布式調(diào)度組件 xxl-job 等實現(xiàn)后續(xù)的處理。

在高并發(fā)的場景下,重試最好使用異步方式,比如發(fā)送消息到 mq 中間件,實現(xiàn)異步解耦。

亦或是利用 Canal 框架訂閱 MySQL binlog 日志,監(jiān)聽對應(yīng)的更新請求,執(zhí)行刪除對應(yīng)緩存操作。

高并發(fā)場景

再來分析下高并發(fā)讀寫會有什么問題……

圖片

先寫數(shù)據(jù)庫后刪緩存

  1. 98 號技師先下手為強(qiáng),接下肖菜雞的這筆生意,數(shù)據(jù)庫執(zhí)行??set 肖菜雞的服務(wù)技師 = 98??;還是網(wǎng)絡(luò)卡頓了下,沒來得及執(zhí)行刪除緩存操作。
  2. 主管 Candy 向系統(tǒng)執(zhí)行讀請求,查下肖菜雞有沒有技師接待,發(fā)現(xiàn)緩存中有數(shù)據(jù)??肖菜雞的服務(wù)技師 = 待定??,直接返回信息給客戶端,主管以為沒人接待。
  3. 原先 98 號技師接單,由于卡頓沒刪除緩存的操作現(xiàn)在執(zhí)行刪除成功。

讀請求可能出現(xiàn)少量讀取舊數(shù)據(jù)的情況,但是很快舊數(shù)據(jù)就會被刪除,之后的請求都能獲取最新數(shù)據(jù),問題不大。

還有一種比較極端的情況,緩存自動失效的時候又遇到了高并發(fā)讀寫的情況,假設(shè)這會有兩個請求,一個線程 A 做查詢操作,一個線程 B 做更新操作,那么會有如下情形產(chǎn)生:

圖片

緩存忽然失效

  1. 緩存的過期時間到期,緩存失效。
  2. 線程 A 讀請求讀取緩存,沒命中,則查詢數(shù)據(jù)庫得到一個舊的值(因為 B 會寫新值,相對而言就是舊的值了),準(zhǔn)備把數(shù)據(jù)寫到緩存時發(fā)送網(wǎng)絡(luò)問題卡頓了。
  3. 線程 B 執(zhí)行寫操作,將新值寫數(shù)據(jù)庫。
  4. 線程 B 執(zhí)行刪除緩存。
  5. 線程 A 繼續(xù),從卡頓中醒來,把查詢到的舊值寫到入緩存。

碼哥,這咋玩,還是出現(xiàn)了不一致的情況啊。

不要慌,發(fā)生這個情況的概率微乎其微,發(fā)生上述情況的必要條件是:

  1. 步驟 (3)的寫數(shù)據(jù)庫操作要比步驟(2)讀操作耗時短速度快,才可能使得步驟(4)先于步驟(5)。
  2. 緩存剛好到達(dá)過期時限。

通常 MySQL 單機(jī)的 QPS 大概 5K 左右,而 TPS 大概 1k 左右,(ps:Tomcat 的 QPS 4K 左右,TPS = 1k 左右)。

數(shù)據(jù)庫讀操作是遠(yuǎn)快于寫操作的(正是因為如此,才做讀寫分離),所以步驟(3)要比步驟(2)更快這個情景很難出現(xiàn),同時還要配合緩存剛好失效。

所以,在用旁路緩存策略的時候,對于寫操作推薦使用:先更新數(shù)據(jù)庫,再刪除緩存。

四、一致性解決方案有哪些?

最后,針對 Cache-Aside (旁路緩存) 策略,寫操作使用先更新數(shù)據(jù)庫,再刪除緩存的情況下,我們來分析下數(shù)據(jù)一致性解決方案都有哪些?

1、緩存延時雙刪

如果采用先刪除緩存,再更新數(shù)據(jù)庫如何避免出現(xiàn)臟數(shù)據(jù)?

采用延時雙刪策略。

  1. 先刪除緩存。
  2. 寫數(shù)據(jù)庫。
  3. 休眠 500 毫秒,再刪除緩存。

這樣子最多只會出現(xiàn) 500 毫秒的臟數(shù)據(jù)讀取時間。關(guān)鍵是這個休眠時間怎么確定呢?

延遲時間的目的就是確保讀請求結(jié)束,寫請求可以刪除讀請求造成的緩存臟數(shù)據(jù)。

所以我們需要自行評估項目的讀數(shù)據(jù)業(yè)務(wù)邏輯的耗時,在讀耗時的基礎(chǔ)上加幾百毫秒作為延遲時間即可。

2、刪除緩存重試機(jī)制

緩存刪除失敗怎么辦?比如延遲雙刪的第二次刪除失敗,那豈不是無法刪除臟數(shù)據(jù)。

使用重試機(jī)制,保證刪除緩存成功。

比如重試三次,三次都失敗則記錄日志到數(shù)據(jù)庫并發(fā)送警告讓人工介入。

在高并發(fā)的場景下,重試最好使用異步方式,比如發(fā)送消息到 mq 中間件,實現(xiàn)異步解耦。

圖片重試機(jī)制

第(5)步如果刪除失敗且未達(dá)到重試最大次數(shù)則將消息重新入隊,直到刪除成功,否則就記錄到數(shù)據(jù)庫,人工介入。

該方案有個缺點,就是對業(yè)務(wù)代碼中造成侵入,于是就有了下一個方案,啟動一個專門訂閱 數(shù)據(jù)庫 binlog 的服務(wù)讀取需要刪除的數(shù)據(jù)進(jìn)行緩存刪除操作。

3、讀取 binlog 異步刪除

圖片

binlog異步刪除

  1. 更新數(shù)據(jù)庫;
  2. 數(shù)據(jù)庫會把操作信息記錄在 binlog 日志中;
  3. 使用 canal 訂閱 binlog 日志獲取目標(biāo)數(shù)據(jù)和 key;
  4. 緩存刪除系統(tǒng)獲取 canal 的數(shù)據(jù),解析目標(biāo) key,嘗試刪除緩存。
  5. 如果刪除失敗則將消息發(fā)送到消息隊列;
  6. 緩存刪除系統(tǒng)重新從消息隊列獲取數(shù)據(jù),再次執(zhí)行刪除操作。

總結(jié)

緩存策略的最佳實踐是 Cache Aside Pattern。分別分為讀緩存最佳實踐和寫緩存最佳實踐。

讀緩存最佳實踐:先讀緩存,命中則返回;未命中則查詢數(shù)據(jù)庫,再寫到數(shù)據(jù)庫。

寫緩存最佳實踐:

  • 先寫數(shù)據(jù)庫,再操作緩存;
  • 直接刪除緩存,而不是修改,因為當(dāng)緩存的更新成本很高,需要訪問多張表聯(lián)合計算,建議直接刪除緩存,而不是更新,另外,刪除緩存操作簡單,副作用只是增加了一次 chache miss,建議大家使用該策略。

在以上最佳實踐下,為了盡可能保證緩存與數(shù)據(jù)庫的一致性,我們可以采用延遲雙刪。

防止刪除失敗,我們采用異步重試機(jī)制保證能正確刪除,異步機(jī)制我們可以發(fā)送刪除消息到 mq 消息中間件,或者利用 canal 訂閱 MySQL binlog 日志監(jiān)聽寫請求刪除對應(yīng)緩存。

那么,如果我非要保證絕對一致性怎么辦,先給出結(jié)論:

沒有辦法做到絕對的一致性,這是由 CAP 理論決定的,緩存系統(tǒng)適用的場景就是非強(qiáng)一致性的場景,所以它屬于 CAP 中的 AP。

所以,我們得委曲求全,可以去做到 BASE 理論中說的最終一致性。

其實一旦在方案中使用了緩存,那往往也就意味著我們放棄了數(shù)據(jù)的強(qiáng)一致性,但這也意味著我們的系統(tǒng)在性能上能夠得到一些提升。

所謂 tradeoff 正是如此。

責(zé)任編輯:姜華 來源: 碼哥字節(jié)
相關(guān)推薦

2016-11-29 09:00:19

分布式數(shù)據(jù)一致性CAS

2024-04-11 13:45:14

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

2023-08-01 07:42:33

Redis數(shù)據(jù)項目

2012-09-24 09:35:42

分布式系統(tǒng)

2023-09-24 14:35:43

Redis數(shù)據(jù)庫

2022-02-17 21:04:27

數(shù)據(jù)庫MysqlRedis

2023-05-26 07:34:50

RedisMySQL緩存

2019-05-27 09:00:00

蘇寧智慧零售平臺數(shù)據(jù)庫

2022-06-21 21:47:13

數(shù)據(jù)系統(tǒng)

2021-12-14 07:15:57

MySQLRedis數(shù)據(jù)

2022-09-15 10:37:46

MySQLRedis數(shù)據(jù)一致性

2023-04-13 08:15:47

Redis緩存一致性

2024-11-07 22:57:30

2021-10-14 10:00:46

MYSQL開發(fā)數(shù)據(jù)

2020-09-04 06:32:08

緩存數(shù)據(jù)庫接口

2024-11-14 07:10:00

2009-06-18 09:18:08

Oracle檢索數(shù)據(jù)數(shù)據(jù)一致性事務(wù)恢復(fù)

2023-12-01 13:51:21

數(shù)據(jù)一致性數(shù)據(jù)庫

2024-08-20 16:13:52

2023-06-29 08:00:59

redis數(shù)據(jù)MySQL
點贊
收藏

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