最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis與MySQL的雙寫(xiě)一致性問(wèn)題

 更新時(shí)間:2023年03月27日 16:41:01   作者:小熊不吃香菜  
這篇文章只要介紹了Redis與MySQL雙寫(xiě)一致性,主要是指在使用緩存和數(shù)據(jù)庫(kù)同時(shí)存儲(chǔ)數(shù)據(jù)的場(chǎng)景下( 主要是存在高并發(fā)的情況),如何保證兩者的數(shù)據(jù)一致性(內(nèi)容相同或者盡可能接近),感興趣的同學(xué)可以借鑒一下

Redis與MySQL雙寫(xiě)一致性是指在使用緩存和數(shù)據(jù)庫(kù)同時(shí)存儲(chǔ)數(shù)據(jù)的場(chǎng)景下( 主要是存在高并發(fā)的情況),如何保證兩者的數(shù)據(jù)一致性(內(nèi)容相同或者盡可能接近)。

 正常業(yè)務(wù)流程

讀沒(méi)什么問(wèn)題,關(guān)鍵就在于寫(xiě)(更新)操作,這就會(huì)出現(xiàn)幾個(gè)問(wèn)題了,這里是先更新數(shù)據(jù)庫(kù),然后對(duì)緩存操作。但對(duì)于緩存操作,是更新緩存還是刪除緩存呢?或者為什么不是先操作(刪除、更新)緩存在更新數(shù)據(jù)庫(kù)呢?

總結(jié)一下就是到底先操作緩存再操作數(shù)據(jù)庫(kù),還是先操作數(shù)據(jù)庫(kù)再操作緩存?

帶著這幾個(gè)問(wèn)題接著往下講。

首先講一下操作緩存,包括兩種:更新緩存和刪除緩存,如何選擇?

更新緩存? 刪除緩存?

假設(shè)都先更新數(shù)據(jù)庫(kù)(因?yàn)橄炔僮骶彺嬖俨僮鲾?shù)據(jù)庫(kù)問(wèn)題較大,后面會(huì)講)

  •  更新緩存

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

如果兩個(gè)請(qǐng)求同時(shí)對(duì)同一條數(shù)據(jù)進(jìn)行修改,那么可能出現(xiàn)先后順序顛倒,導(dǎo)致緩存中的數(shù)據(jù)是舊的。之后的讀請(qǐng)求讀到的都是舊數(shù)據(jù),只有當(dāng)緩存失效后,才能從數(shù)據(jù)庫(kù)中得到正確的值。

  • 刪除緩存

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

會(huì)有這樣一種情況:緩存剛好失效,請(qǐng)求B從數(shù)據(jù)庫(kù)中查詢數(shù)據(jù),得到舊值。此時(shí)請(qǐng)求A更新數(shù)據(jù)庫(kù),將新值寫(xiě)入數(shù)據(jù)庫(kù),并刪除緩存。而請(qǐng)求B又將舊值寫(xiě)入緩存中,導(dǎo)致臟數(shù)據(jù)

從上面看出現(xiàn)臟數(shù)據(jù)的要求要比更新緩存的要求更多,必須滿足以下幾個(gè)條件:

  1. 緩存失效
  2. 讀請(qǐng)求 + 寫(xiě)請(qǐng)求并發(fā)
  3. 更新數(shù)據(jù)庫(kù) + 刪除緩存的時(shí)間要比讀數(shù)據(jù)庫(kù) + 寫(xiě)緩存時(shí)間

前面兩個(gè)很好滿足,我們?cè)倏纯吹谌c(diǎn),這個(gè)真的會(huì)出現(xiàn)嗎?

數(shù)據(jù)庫(kù)在更新時(shí)一般是加鎖的,讀操作的速度遠(yuǎn)快于寫(xiě)操作的,所以第三點(diǎn)發(fā)生概率極低(當(dāng)然也可能發(fā)生)

注:這里我其實(shí)不是很理解,單純看確實(shí)發(fā)生概率低,但如果出現(xiàn)網(wǎng)絡(luò)延遲等情況呢,不也會(huì)發(fā)生嗎?希望好心人解惑,我反正沒(méi)理解。

因此,在選擇刪除緩存時(shí),還需要結(jié)合其他技術(shù)來(lái)優(yōu)化性能和一致性。例如:

  • 使用消息隊(duì)列來(lái)異步地刪除或更新緩存,避免阻塞主線程或者丟失消息。
  • 使用延時(shí)雙刪來(lái)增加刪除成功率和減少不一致時(shí)間窗口。即在更新數(shù)據(jù)庫(kù)后立即刪除一次緩存,并在一定時(shí)間間隔后再次刪除一次。

 對(duì)比

在更新緩存中, 每次去更新緩存,但是緩存中的數(shù)據(jù)不一定會(huì)被馬上讀取,這就會(huì)導(dǎo)致緩存中可能存放了很多不常訪問(wèn)的數(shù)據(jù),浪費(fèi)緩存資源。而且很多情況下,寫(xiě)到緩存中的值,并不是與數(shù)據(jù)庫(kù)中的值一一對(duì)應(yīng)的,很有可能是先查詢數(shù)據(jù)庫(kù),再經(jīng)過(guò)一系列「計(jì)算」得出一個(gè)值,才把這個(gè)值才寫(xiě)到緩存中。

? 由此可見(jiàn),這種更新緩存的方案,不僅緩存利用率不高,還會(huì)造成機(jī)器性能的浪費(fèi)。所以我們一般考慮刪除緩存

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

在更新數(shù)據(jù)時(shí),先將新數(shù)據(jù)寫(xiě)入緩存(Redis),再將新數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)(MySQL)

 但其存在一下問(wèn)題:

  • 緩存更新成功,但數(shù)據(jù)庫(kù)更新失敗,導(dǎo)致數(shù)據(jù)不一致

 :用戶修改了自己的昵稱,系統(tǒng)先將新的昵稱寫(xiě)入緩存,然后再更新數(shù)據(jù)庫(kù)。但是在更新數(shù)據(jù)庫(kù)的過(guò)程中,發(fā)生了網(wǎng)絡(luò)故障或者數(shù)據(jù)庫(kù)宕機(jī)等異常情況,導(dǎo)致數(shù)據(jù)庫(kù)中的昵稱沒(méi)有被修改。這樣就會(huì)出現(xiàn)緩存中的昵稱和數(shù)據(jù)庫(kù)中的昵稱不一致的情況。

  • 緩存更新成功,但數(shù)據(jù)庫(kù)更新延遲,導(dǎo)致其他請(qǐng)求讀取到舊的數(shù)據(jù)

 :用戶下單了一個(gè)商品,系統(tǒng)先將訂單狀態(tài)寫(xiě)入緩存,然后再更新數(shù)據(jù)庫(kù)。但是在更新數(shù)據(jù)庫(kù)的過(guò)程中,由于并發(fā)量大或者其他原因,導(dǎo)致數(shù)據(jù)庫(kù)的寫(xiě)入速度慢于緩存的寫(xiě)入速度。這樣就會(huì)出現(xiàn)其他請(qǐng)求從緩存中讀取到訂單狀態(tài)為已支付,而從數(shù)據(jù)庫(kù)中讀取到訂單狀態(tài)為未支付的情況。

  • 緩存更新成功,但在數(shù)據(jù)庫(kù)更新之前有其他請(qǐng)求查詢了緩存和數(shù)據(jù)庫(kù),并將舊的數(shù)據(jù)寫(xiě)回緩存,覆蓋了新的數(shù)據(jù)

 例:用戶A修改了自己的頭像,并上傳到服務(wù)器上。系統(tǒng)先將新的頭像地址寫(xiě)入緩存,并返回給用戶A顯示。然后再將新的頭像地址更新到數(shù)據(jù)庫(kù)中。但是在這個(gè)過(guò)程中,用戶B訪問(wèn)了用戶A的個(gè)人主頁(yè),并從緩存中讀取到了新的頭像地址。由于緩存失效策略或者其他原因(比如重啟),導(dǎo)致緩存被清空或者過(guò)期。這時(shí)候用戶B再次訪問(wèn)用戶A 的個(gè)人主頁(yè),并從數(shù)據(jù)庫(kù)中讀取到了舊的頭像地址,并將其寫(xiě)回緩存中。這樣就會(huì)出現(xiàn)緩存中 的頭像地址和 數(shù)據(jù)庫(kù) 中 的頭像地址不一致 的情況。

上面說(shuō)了一堆,其實(shí)總結(jié)就是緩存更新成功了,數(shù)據(jù)庫(kù)沒(méi)更新(更新失敗),導(dǎo)致緩存存的是最新值,數(shù)據(jù)庫(kù)存的是舊值。如果緩存失效了,就會(huì)拿到數(shù)據(jù)庫(kù)中的舊值。

 后面我自己也搞疑惑了,既然是因?yàn)閿?shù)據(jù)庫(kù)更新失敗導(dǎo)致的問(wèn)題,那我是不是只要保證數(shù)據(jù)庫(kù)更新成功就可以解決數(shù)據(jù)不一致的問(wèn)題,當(dāng)數(shù)據(jù)庫(kù)更新失敗時(shí),不停的重試更新數(shù)據(jù)庫(kù),直到數(shù)據(jù)庫(kù)更新完成。

后面發(fā)現(xiàn)自己太天真,其中存在很多問(wèn)題,比如:

  • 如果數(shù)據(jù)庫(kù)更新失敗的原因是數(shù)據(jù)庫(kù)宕機(jī)或者網(wǎng)絡(luò)故障,那么你不停地重試更新數(shù)據(jù)庫(kù)可能會(huì)造成更大的壓力和延遲,甚至導(dǎo)致數(shù)據(jù)庫(kù)恢復(fù)困難。
  • 如果數(shù)據(jù)庫(kù)更新失敗的原因是數(shù)據(jù)沖突或者業(yè)務(wù)邏輯錯(cuò)誤,那么你不停地重試更新數(shù)據(jù)庫(kù)可能會(huì)導(dǎo)致數(shù)據(jù)丟失或者數(shù)據(jù)錯(cuò)亂,甚至影響其他用戶的數(shù)據(jù)。
  • 如果你不停地重試更新數(shù)據(jù)庫(kù),那么你需要考慮如何保證重試的冪等性和順序性,以及如何處理重試過(guò)程中發(fā)生的異常情況。

 所以,這種方法并不是一個(gè)很好的解決方案。

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

當(dāng)有一個(gè)更新操作時(shí),先更新數(shù)據(jù)庫(kù)數(shù)據(jù),然后再更新對(duì)應(yīng)的緩存數(shù)據(jù)

 但是,這種方案也有一些問(wèn)題和風(fēng)險(xiǎn),比如:

  • 如果更新數(shù)據(jù)庫(kù)成功了,但是更新緩存失敗了,那么就會(huì)導(dǎo)致緩存中就會(huì)保留舊的數(shù)據(jù),而數(shù)據(jù)庫(kù)中已經(jīng)是新的數(shù)據(jù),即臟數(shù)據(jù)。
  • 如果在更新數(shù)據(jù)庫(kù)和更新緩存之間,有其他請(qǐng)求查詢了同一個(gè)數(shù)據(jù),并且發(fā)現(xiàn)緩存存在,那么就會(huì)從緩存中讀取舊的數(shù)據(jù)。這樣也會(huì)造成緩存和數(shù)據(jù)庫(kù)之間的不一致性。

 因此,在使用更新緩存操作時(shí),無(wú)論誰(shuí)先誰(shuí)后,但凡后者發(fā)生異常,就會(huì)對(duì)業(yè)務(wù)造成影響。(還是上面那張圖)

那么如何處理異常情況來(lái)保證數(shù)據(jù)一致性呢

這些問(wèn)題的源頭都是多線程并發(fā)所導(dǎo)致的,所以最簡(jiǎn)單的方法就是加鎖(分布式鎖)。兩個(gè)線程要修改同一條數(shù)據(jù),每個(gè)線程在改之前,先去申請(qǐng)分布式鎖,拿到鎖的線程才允許更新數(shù)據(jù)庫(kù)和緩存,拿不到鎖的線程,返回失敗,等待下次重試。這么做的目的,就是為了只允許一個(gè)線程去操作數(shù)據(jù)和緩存,避免并發(fā)問(wèn)題。

? 但加鎖費(fèi)時(shí)費(fèi)力,肯定不推薦。并且,每次去更新緩存,但是緩存中的數(shù)據(jù)不一定會(huì)被馬上讀取,這就會(huì)導(dǎo)致緩存中可能存放了很多不常訪問(wèn)的數(shù)據(jù),浪費(fèi)緩存資源。而且很多情況下,寫(xiě)到緩存中的值,并不是與數(shù)據(jù)庫(kù)中的值一一對(duì)應(yīng)的,很有可能是先查詢數(shù)據(jù)庫(kù),再經(jīng)過(guò)一系列「計(jì)算」得出一個(gè)值,才把這個(gè)值才寫(xiě)到緩存中。

? 由此可見(jiàn),這種更新數(shù)據(jù)庫(kù) + 更新緩存的方案,不僅緩存利用率不高,還會(huì)造成機(jī)器性能的浪費(fèi)。

所以此時(shí)我們需要考慮另外一種方案:刪除緩存

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

當(dāng)有一個(gè)更新操作時(shí),先刪除對(duì)應(yīng)的緩存數(shù)據(jù),然后再更新數(shù)據(jù)庫(kù)數(shù)據(jù)

 但是,這種方案也有一些問(wèn)題和風(fēng)險(xiǎn),比如:

  • 如果刪除緩存后,更新數(shù)據(jù)庫(kù)失敗了,那么就會(huì)導(dǎo)致緩存丟失,下次查詢時(shí)需要重新從數(shù)據(jù)庫(kù)加載數(shù)據(jù),增加了數(shù)據(jù)庫(kù)壓力和響應(yīng)時(shí)間。
  • 如果在刪除緩存和更新數(shù)據(jù)庫(kù)之間,有其他請(qǐng)求查詢了同一個(gè)數(shù)據(jù),并且發(fā)現(xiàn)緩存不存在,那么就會(huì)從數(shù)據(jù)庫(kù)中讀取舊的數(shù)據(jù),并寫(xiě)入到緩存中。這樣就會(huì)造成緩存和數(shù)據(jù)庫(kù)之間的不一致性。

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

當(dāng)有一個(gè)更新操作時(shí),先更新數(shù)據(jù)庫(kù)數(shù)據(jù),再刪除緩存

上面其實(shí)講過(guò)了,我再重復(fù)一遍吧

會(huì)有這樣一種情況:緩存剛好失效,請(qǐng)求B從數(shù)據(jù)庫(kù)中查詢數(shù)據(jù),得到舊值。此時(shí)請(qǐng)求A更新數(shù)據(jù)庫(kù),將新值寫(xiě)入數(shù)據(jù)庫(kù),并刪除緩存。而請(qǐng)求B又將舊值寫(xiě)入緩存中,導(dǎo)致臟數(shù)據(jù)

從上面看出現(xiàn)臟數(shù)據(jù)的要求要比更新緩存的要求更多,必須滿足以下幾個(gè)條件:

  1. 緩存失效
  2. 讀請(qǐng)求 + 寫(xiě)請(qǐng)求并發(fā)
  3. 更新數(shù)據(jù)庫(kù) + 刪除緩存的時(shí)間要比讀數(shù)據(jù)庫(kù) + 寫(xiě)緩存時(shí)間

前面兩個(gè)很好滿足,我們?cè)倏纯吹谌c(diǎn),這個(gè)真的會(huì)出現(xiàn)嗎?

數(shù)據(jù)庫(kù)在更新時(shí)一般是加鎖的,讀操作的速度遠(yuǎn)快于寫(xiě)操作的,所以第三點(diǎn)發(fā)生概率極低

所以,解決雙寫(xiě)問(wèn)題更適合的方法是先更新數(shù)據(jù)庫(kù),再刪除緩存,當(dāng)然具體場(chǎng)景具體分析,不定說(shuō)一定就是這個(gè)。

講解了這些操作后會(huì)出現(xiàn)的問(wèn)題,那么為了避免這些問(wèn)題,如何做呢?

  • 先刪除緩存再更新數(shù)據(jù)庫(kù),然后使用異步線程或消息隊(duì)列來(lái)重建緩存。
  • 先更新數(shù)據(jù)庫(kù)再刪除緩存,并設(shè)置一個(gè)合理的過(guò)期時(shí)間來(lái)保證緩存的有效性。
  • 使用分布式鎖或樂(lè)觀鎖來(lái)控制并發(fā)訪問(wèn),并保證每次只有一個(gè)請(qǐng)求能夠操作緩存和數(shù)據(jù)庫(kù)

 ……

下面講幾種常見(jiàn)的方法以保證雙寫(xiě)一致性

解決方案

1. 重試

上面也提到過(guò),當(dāng)?shù)诙讲僮魇r(shí),我就重試嘛,盡可能地補(bǔ)救,但重試的成本太大,上面講過(guò)就不重復(fù)了。

2. 異步重試

? 既然重試方法占用資源,那我就做異步。在刪除或更新緩存時(shí),如果操作失敗,不立即返回錯(cuò)誤,而是通過(guò)一些機(jī)制(如消息隊(duì)列、定時(shí)任務(wù)、訂閱binlog等)來(lái)觸發(fā)緩存的重試操作。這樣可以避免同步重試緩存時(shí)的性能損耗和阻塞問(wèn)題,但也可能導(dǎo)致緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致的時(shí)間較長(zhǎng)。

2.1 使用消息隊(duì)列實(shí)現(xiàn)重試

  • 消息隊(duì)列保證可靠性:寫(xiě)到隊(duì)列中的消息,成功消費(fèi)之前不會(huì)丟失(重啟項(xiàng)目也不擔(dān)心)
  • 消息隊(duì)列保證消息成功投遞:下游從隊(duì)列拉取消息,成功消費(fèi)后才會(huì)刪除消息,否則還會(huì)繼續(xù)投遞消息給消費(fèi)者(符合我們重試的需求)

使用消息隊(duì)列異步重試緩存的情況是指,當(dāng)信息發(fā)生變化時(shí),先更新數(shù)據(jù)庫(kù),然后刪緩存,如果刪除成功就皆大歡喜,如果刪除失敗,則將需要?jiǎng)h除的key發(fā)送到消息隊(duì)列。另外有一個(gè)消費(fèi)者線程從消息隊(duì)列中獲取要?jiǎng)h除的key,并根據(jù)key刪除或更新Redis中的緩存。如果操作失敗,則重新發(fā)送到消息隊(duì)列中進(jìn)行重試。

注:也可以不先嘗試刪除,直接發(fā)送給消息隊(duì)列,讓消息隊(duì)列

舉例來(lái)說(shuō),假設(shè)有一個(gè)用戶信息表,需要將用戶信息緩存在Redis中。如果采用使用消息隊(duì)列異步重試緩存的方案,可以有以下幾個(gè)步驟:

  • 當(dāng)用戶信息發(fā)生變化時(shí),先更新數(shù)據(jù)庫(kù),并返回成功結(jié)果給前端。
  • 嘗試去刪除緩存,成功則結(jié)束操作,失敗則將要?jiǎng)h除或更新緩存的操作生成一個(gè)消息(比如包含key和操作類型),并發(fā)送到消息隊(duì)列中(比如使用Kafka或RabbitMQ)。
  • 另外有一個(gè)消費(fèi)者線程從消息隊(duì)列中訂閱并獲取這些消息,并根據(jù)消息內(nèi)容刪除或更新Redis中的對(duì)應(yīng)信息。
  • 如果刪除或更新緩存成功,則把這個(gè)消息從消息隊(duì)列中移除(丟棄),以免重復(fù)操作。
  • 如果刪除或更新緩存失敗,則執(zhí)行失敗策略,比如設(shè)置一個(gè)延遲時(shí)間或者一個(gè)重試次數(shù)限制,然后重新發(fā)送這個(gè)消息到消息隊(duì)列中進(jìn)行重試。
  • 如果重試超過(guò)一定次數(shù)仍然失敗,則向業(yè)務(wù)層發(fā)送報(bào)錯(cuò)信息,并記錄日志。

2.2 Binlog實(shí)現(xiàn)異步重試刪除

使用binlog實(shí)現(xiàn)一致性的基本思路是利用binlog日志來(lái)記錄數(shù)據(jù)庫(kù)的變更操作,然后通過(guò)主從復(fù)制或者增量備份的方式來(lái)同步或者恢復(fù)數(shù)據(jù)。

舉例來(lái)說(shuō),如果我們有一個(gè)主數(shù)據(jù)庫(kù)和一個(gè)從數(shù)據(jù)庫(kù),我們可以在主數(shù)據(jù)庫(kù)上開(kāi)啟binlog日志,并設(shè)置從數(shù)據(jù)庫(kù)作為它的復(fù)制節(jié)點(diǎn)。這樣,當(dāng)主數(shù)據(jù)庫(kù)上發(fā)生任何變更操作時(shí),它會(huì)將對(duì)應(yīng)的binlog日志發(fā)送給從數(shù)據(jù)庫(kù),從數(shù)據(jù)庫(kù)則會(huì)根據(jù)binlog日志來(lái)執(zhí)行相同的操作,從而保證數(shù)據(jù)一致性。

另外,如果我們需要恢復(fù)某個(gè)時(shí)間點(diǎn)之前的數(shù)據(jù),我們也可以利用binlog日志來(lái)實(shí)現(xiàn)。首先,我們需要找到對(duì)應(yīng)時(shí)間點(diǎn)之前的最近一個(gè)全量備份文件,并將其恢復(fù)到目標(biāo)數(shù)據(jù)庫(kù)。然后,我們需要找到對(duì)應(yīng)時(shí)間點(diǎn)之前的所有增量備份文件(即binlog日志文件),并按照順序?qū)⑵鋺?yīng)用到目標(biāo)數(shù)據(jù)庫(kù)。這樣,我們就可以恢復(fù)出目標(biāo)時(shí)間點(diǎn)之前的數(shù)據(jù)狀態(tài)了。

  • 使用 Binlog 實(shí)時(shí)更新/刪除 Redis 緩存。利用 Canal,即將負(fù)責(zé)更新緩存的服務(wù)偽裝成一個(gè) MySQL 的從節(jié)點(diǎn),從 MySQL 接收 Binlog,解析 Binlog 之后,得到實(shí)時(shí)的數(shù)據(jù)變更信息,然后根據(jù)變更信息去更新/刪除 Redis 緩存;
  • MQ+Canal 策略,將 Canal Server 接收到的 Binlog 數(shù)據(jù)直接投遞到 MQ 進(jìn)行解耦,使用 MQ 異步消費(fèi) Binlog 日志,以此進(jìn)行數(shù)據(jù)同步;

注:binlog日志是MySQL的二進(jìn)制日志,它記錄了對(duì)數(shù)據(jù)庫(kù)的變更操作,比如插入、更新、刪除等。 binlog日志有兩個(gè)主要作用,一個(gè)是主從復(fù)制,另一個(gè)是增量備份。

主從復(fù)制是指在一個(gè)主數(shù)據(jù)庫(kù)和一個(gè)或多個(gè)從數(shù)據(jù)庫(kù)之間實(shí)現(xiàn)數(shù)據(jù)的同步。主數(shù)據(jù)庫(kù)會(huì)將自己的binlog日志發(fā)送給從數(shù)據(jù)庫(kù),從數(shù)據(jù)庫(kù)則會(huì)根據(jù)binlog日志來(lái)執(zhí)行相同的操作,從而保證數(shù)據(jù)一致性。這樣可以提高數(shù)據(jù)的可用性和可靠性,也可以實(shí)現(xiàn)負(fù)載均衡和故障轉(zhuǎn)移。

增量備份是指在全量備份的基礎(chǔ)上,定期備份數(shù)據(jù)庫(kù)的變更操作。全量備份是指將整個(gè)數(shù)據(jù)庫(kù)的數(shù)據(jù)完整地備份到一個(gè)文件中。增量備份則是指將每次變更操作對(duì)應(yīng)的binlog日志文件備份到另一個(gè)文件中。這樣可以減少備份所占用的空間和時(shí)間,也可以實(shí)現(xiàn)靈活地恢復(fù)數(shù)據(jù)到任意時(shí)間點(diǎn)。

至此,我們可以得出結(jié)論,想要保證數(shù)據(jù)庫(kù)和緩存一致性,推薦采用「先更新數(shù)據(jù)庫(kù),再刪除緩存」方案,并配合「消息隊(duì)列」或「訂閱變更日志」的方式來(lái)做。

3. 延時(shí)雙刪

我們重點(diǎn)在將先更新數(shù)據(jù)庫(kù),在刪除緩存。那如果我要先刪除緩存,再更新數(shù)據(jù)庫(kù)呢?

回顧之前講的先刪除緩存,再更新數(shù)據(jù)庫(kù),它會(huì)出現(xiàn)舊值覆蓋緩存的問(wèn)題,那好辦,我們直接把這個(gè)舊值給刪了不就完了嗎,延時(shí)雙刪就是這個(gè)原理,它的基本思路是:

  1. 先刪除緩存
  2. 再更新數(shù)據(jù)庫(kù)
  3. 休眠一段時(shí)間(根據(jù)系統(tǒng)情況確定)
  4. 再次刪除緩存

這樣做的目的是為了防止在更新數(shù)據(jù)庫(kù)后,有其他線程讀取到舊的緩存數(shù)據(jù),并將其寫(xiě)回緩存,導(dǎo)致數(shù)據(jù)不一致。

舉個(gè)例子:假設(shè)有一個(gè)用戶信息表,其中有一個(gè)字段是用戶積分?,F(xiàn)在有兩個(gè)線程A和B同時(shí)對(duì)用戶積分進(jìn)行操作:

  • 線程A要給用戶增加100積分
  • 線程B要給用戶減少50積分

如果使用延時(shí)雙刪策略,那么線程A和B的執(zhí)行過(guò)程可能如下:

  • 線程A先刪除緩存中的用戶信息
  • 線程A再?gòu)臄?shù)據(jù)庫(kù)中讀取用戶信息,發(fā)現(xiàn)用戶積分為1000
  • 線程A將用戶積分加上100,變?yōu)?100,并更新到數(shù)據(jù)庫(kù)中
  • 線程A休眠5秒(假設(shè)這個(gè)時(shí)間足夠讓數(shù)據(jù)庫(kù)同步)
  • 線程A再次刪除緩存中的用戶信息
  • 線程B先刪除緩存中的用戶信息
  • 線程B再?gòu)臄?shù)據(jù)庫(kù)中讀取用戶信息,發(fā)現(xiàn)用戶積分為1100(因?yàn)榫€程A已經(jīng)更新了)
  • 線程B將用戶積分減去50,變?yōu)?050,并更新到數(shù)據(jù)庫(kù)中
  • 線程B休眠5秒(假設(shè)這個(gè)時(shí)間足夠讓數(shù)據(jù)庫(kù)同步)
  • 線程B再次刪除緩存中的用戶信息

這樣最終結(jié)果就是:數(shù)據(jù)庫(kù)中的用戶積分為1050,緩存中沒(méi)有該用戶信息。當(dāng)下次有請(qǐng)求查詢?cè)撚脩粜畔r(shí),就會(huì)從數(shù)據(jù)庫(kù)中讀取并寫(xiě)入到緩存中。這樣就保證了數(shù)據(jù)一致性。

延時(shí)雙刪適用于高并發(fā)場(chǎng)景,特別是對(duì)數(shù)據(jù)的修改操作比較頻繁,而查詢操作比較少的情況。這樣可以減輕數(shù)據(jù)庫(kù)的壓力,提高性能,同時(shí)保證數(shù)據(jù)的最終一致性。延時(shí)雙刪也適用于數(shù)據(jù)庫(kù)有主從同步延遲的場(chǎng)景,因?yàn)樗梢员苊庠诟聰?shù)據(jù)庫(kù)后,從庫(kù)還沒(méi)有同步完成時(shí),讀取到舊的緩存數(shù)據(jù),并將其寫(xiě)回緩存。

注: 這個(gè)休眠時(shí)間 = 讀業(yè)務(wù)邏輯數(shù)據(jù)的耗時(shí) + 幾百毫秒。 為了確保讀請(qǐng)求結(jié)束,寫(xiě)請(qǐng)求可以刪除讀請(qǐng)求可能帶來(lái)的緩存臟數(shù)據(jù)。

總結(jié)

好了,總結(jié)一下這篇文章的重點(diǎn)。

Redis與MySQL的雙寫(xiě)一致性問(wèn)題是指在使用緩存和數(shù)據(jù)庫(kù)同時(shí)存儲(chǔ)數(shù)據(jù)的場(chǎng)景下,如何保證兩者的數(shù)據(jù)一致性。這個(gè)問(wèn)題主要涉及到以下幾個(gè)方面:

  • 緩存更新策略:緩存更新策略有三種,分別是先更新緩存再更新數(shù)據(jù)庫(kù),先更新數(shù)據(jù)庫(kù)再更新緩存,先刪除緩存再更新數(shù)據(jù)庫(kù) 和先更新數(shù)據(jù)庫(kù)再刪除緩存。每種策略都有可能導(dǎo)致數(shù)據(jù)不一致的情況。
  • 數(shù)據(jù)庫(kù)主從同步延遲:如果使用了主從復(fù)制模式來(lái)提高數(shù)據(jù)庫(kù)的可用性和讀寫(xiě)分離能力,那么就可能存在主從同步延遲的問(wèn)題。也就是說(shuō),在主庫(kù)上執(zhí)行了寫(xiě)操作后,并不會(huì)立即同步到從庫(kù)上。這樣,在讀取數(shù)據(jù)時(shí),如果從主庫(kù)讀取,則可能獲取到最新的數(shù)據(jù);而如果從從庫(kù)讀取,則可能獲取到舊的數(shù)據(jù)。這樣也會(huì)導(dǎo)致與緩存中的數(shù)據(jù)不一致。

為了解決這些問(wèn)題 , 可以采用以下幾種方法 :

  • 采用先刪除緩存,再更新數(shù)據(jù)庫(kù)方案,在并發(fā)場(chǎng)景下依舊有不一致問(wèn)題,解決方案是延遲雙刪,但這個(gè)延遲時(shí)間很難評(píng)估。
  • 采用先更新數(shù)據(jù)庫(kù),再刪除緩存方案,為了保證兩步都成功執(zhí)行,需配合消息隊(duì)列或訂閱變更日志的方案來(lái)做,本質(zhì)是通過(guò)重試的方式保證數(shù)據(jù)最終一致性。
  • 采用先更新數(shù)據(jù)庫(kù),再刪除緩存方案,讀寫(xiě)分離 + 主從庫(kù)延遲也會(huì)導(dǎo)致緩存和數(shù)據(jù)庫(kù)不一致,緩解此問(wèn)題的方案是延遲雙刪,憑借經(jīng)驗(yàn)發(fā)送延遲消息到隊(duì)列中,延遲刪除緩存,同時(shí)也要控制主從庫(kù)延遲,盡可能降低不一致發(fā)生的概率。

總之,根據(jù)場(chǎng)景選擇適合自己的方案

 以上就是Redis與MySQL的雙寫(xiě)一致性問(wèn)題的詳細(xì)內(nèi)容,更多關(guān)于Redis與MySQL的一致性的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • MySQL刪除表操作實(shí)現(xiàn)(delete、truncate、drop的區(qū)別)

    MySQL刪除表操作實(shí)現(xiàn)(delete、truncate、drop的區(qū)別)

    這篇文章主要介紹了MySQL刪除表操作實(shí)現(xiàn)(delete、truncate、drop的區(qū)別),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-12-12
  • MySQL判斷時(shí)間段是否重合的兩種方法

    MySQL判斷時(shí)間段是否重合的兩種方法

    這篇文章介紹了MySQL判斷時(shí)間段是否重合的兩種方法,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-07-07
  • MySql?字符集不同導(dǎo)致?left?join?慢查詢的問(wèn)題解決

    MySql?字符集不同導(dǎo)致?left?join?慢查詢的問(wèn)題解決

    當(dāng)兩個(gè)表的字符集不一樣,在使用字符型字段進(jìn)行表連接查詢時(shí),就需要特別注意下查詢耗時(shí)是否符合預(yù)期,本文主要介紹了MySql?字符集不同導(dǎo)致?left?join?慢查詢的問(wèn)題解決,感興趣的可以了解一下
    2024-05-05
  • Mysql避免重復(fù)插入數(shù)據(jù)的4種方式

    Mysql避免重復(fù)插入數(shù)據(jù)的4種方式

    這篇文章主要介紹了Mysql避免重復(fù)插入數(shù)據(jù)的4種方式,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2021-02-02
  • mysql 無(wú)限級(jí)分類實(shí)現(xiàn)思路

    mysql 無(wú)限級(jí)分類實(shí)現(xiàn)思路

    關(guān)于該問(wèn)題,暫時(shí)自己還沒(méi)有深入研究,在網(wǎng)上找到幾種解決方案,各有優(yōu)缺點(diǎn)。
    2011-08-08
  • MySQL中case?when對(duì)NULL值判斷的踩坑記錄

    MySQL中case?when對(duì)NULL值判斷的踩坑記錄

    最近在學(xué)習(xí)Hive基礎(chǔ)知識(shí)時(shí),遇到了遇到了Case?When?Else?End語(yǔ)法,這篇文章主要給大家介紹了關(guān)于MySQL中case?when對(duì)NULL值判斷的踩坑記錄,需要的朋友可以參考下
    2021-12-12
  • MySQL中B樹(shù)索引和B+樹(shù)索引的區(qū)別詳解

    MySQL中B樹(shù)索引和B+樹(shù)索引的區(qū)別詳解

    這篇文章主要為大家詳細(xì)介紹了MySQL中B樹(shù)索引和B+樹(shù)索引的區(qū)別,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來(lái)幫助
    2022-03-03
  • MySQL?原理優(yōu)化之Group?By的優(yōu)化技巧

    MySQL?原理優(yōu)化之Group?By的優(yōu)化技巧

    這篇文章主要介紹了MySQL?原理優(yōu)化之Group?By的優(yōu)化技巧,文章圍繞主題展開(kāi)詳細(xì)的內(nèi)容介紹,具有一定的參考價(jià)值,需要的小伙伴可以參考一下
    2022-08-08
  • MySQL中查詢和展示LONGBLOB類型數(shù)據(jù)的技巧總結(jié)

    MySQL中查詢和展示LONGBLOB類型數(shù)據(jù)的技巧總結(jié)

    在MySQL中LONGBLOB是一種二進(jìn)制大對(duì)象(BLOB)數(shù)據(jù)類型,用于存儲(chǔ)大量的二進(jìn)制數(shù)據(jù),這篇文章主要介紹了MySQL中查詢和展示LONGBLOB類型數(shù)據(jù)的相關(guān)資料,需要的朋友可以參考下
    2025-08-08
  • MySQL 5.7.27下載安裝配置的詳細(xì)教程

    MySQL 5.7.27下載安裝配置的詳細(xì)教程

    這篇文章主要介紹了MySQL 5.7.27詳細(xì)下載安裝配置教程,本文通過(guò)圖文并茂的形式給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-08-08

最新評(píng)論

临夏市| 海丰县| 九江市| 军事| 广灵县| 怀来县| 广州市| 辉南县| 刚察县| 红河县| 曲水县| 景东| 秭归县| 镇宁| 祁连县| 全州县| 洪江市| 周口市| 汤阴县| 武山县| 漳州市| 深圳市| 淮北市| 建始县| 武定县| 遵义县| 广水市| 黎川县| 许昌县| 大埔县| 唐山市| 马公市| 峡江县| 玉屏| 黄龙县| 乃东县| 调兵山市| 榆林市| 裕民县| 娄底市| 玉林市|