<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          如何保證數(shù)據(jù)庫(kù)和緩存雙寫(xiě)一致性?

          共 5782字,需瀏覽 12分鐘

           ·

          2022-04-08 20:57

          大家好,我是龍臺(tái),又跟大家見(jiàn)面了。

          前言

          數(shù)據(jù)庫(kù)和緩存(比如:redis)雙寫(xiě)數(shù)據(jù)一致性問(wèn)題,是一個(gè)跟開(kāi)發(fā)語(yǔ)言無(wú)關(guān)的公共問(wèn)題。尤其在高并發(fā)的場(chǎng)景下,這個(gè)問(wèn)題變得更加嚴(yán)重。

          我很負(fù)責(zé)的告訴大家,該問(wèn)題無(wú)論在面試,還是工作中遇到的概率非常大,所以非常有必要跟大家一起探討一下。

          今天這篇文章我會(huì)從淺入深,跟大家一起聊聊,數(shù)據(jù)庫(kù)和緩存雙寫(xiě)數(shù)據(jù)一致性問(wèn)題常見(jiàn)的解決方案,這些方案中可能存在的坑,以及最優(yōu)方案是什么。

          1. 常見(jiàn)方案

          通常情況下,我們使用緩存的主要目的是為了提升查詢(xún)的性能。大多數(shù)情況下,我們是這樣使用緩存的:

          1. 用戶(hù)請(qǐng)求過(guò)來(lái)之后,先查緩存有沒(méi)有數(shù)據(jù),如果有則直接返回。
          2. 如果緩存沒(méi)數(shù)據(jù),再繼續(xù)查數(shù)據(jù)庫(kù)。
          3. 如果數(shù)據(jù)庫(kù)有數(shù)據(jù),則將查詢(xún)出來(lái)的數(shù)據(jù),放入緩存中,然后返回該數(shù)據(jù)。
          4. 如果數(shù)據(jù)庫(kù)也沒(méi)數(shù)據(jù),則直接返回空。

          這是緩存非常常見(jiàn)的用法。一眼看上去,好像沒(méi)有啥問(wèn)題。

          但你忽略了一個(gè)非常重要的細(xì)節(jié):如果數(shù)據(jù)庫(kù)中的某條數(shù)據(jù),放入緩存之后,又立馬被更新了,那么該如何更新緩存呢?

          不更新緩存行不行?

          答:當(dāng)然不行,如果不更新緩存,在很長(zhǎng)的一段時(shí)間內(nèi)(決定于緩存的過(guò)期時(shí)間),用戶(hù)請(qǐng)求從緩存中獲取到的都可能是舊值,而非數(shù)據(jù)庫(kù)的最新值。這不是有數(shù)據(jù)不一致的問(wèn)題?

          那么,我們?cè)撊绾胃戮彺婺兀?/p>

          目前有以下4種方案:

          1. 先寫(xiě)緩存,再寫(xiě)數(shù)據(jù)庫(kù)
          2. 先寫(xiě)數(shù)據(jù)庫(kù),再寫(xiě)緩存
          3. 先刪緩存,再寫(xiě)數(shù)據(jù)庫(kù)
          4. 先寫(xiě)數(shù)據(jù)庫(kù),再刪緩存

          接下來(lái),我們?cè)敿?xì)說(shuō)說(shuō)這4種方案。

          2. 先寫(xiě)緩存,再寫(xiě)數(shù)據(jù)庫(kù)

          對(duì)于更新緩存的方案,很多人第一個(gè)想到的可能是在寫(xiě)操作中直接更新緩存(寫(xiě)緩存),更直接明了。

          那么,問(wèn)題來(lái)了:在寫(xiě)操作中,到底是先寫(xiě)緩存,還是先寫(xiě)數(shù)據(jù)庫(kù)呢?

          我們?cè)谶@里先聊聊先寫(xiě)緩存,再寫(xiě)數(shù)據(jù)庫(kù)的情況,因?yàn)樗膯?wèn)題最嚴(yán)重。某一個(gè)用戶(hù)的每一次寫(xiě)操作,如果剛寫(xiě)完緩存,突然網(wǎng)絡(luò)出現(xiàn)了異常,導(dǎo)致寫(xiě)數(shù)據(jù)庫(kù)失敗了。其結(jié)果是緩存更新成了最新數(shù)據(jù),但數(shù)據(jù)庫(kù)沒(méi)有,這樣緩存中的數(shù)據(jù)不就變成臟數(shù)據(jù)了?如果此時(shí)該用戶(hù)的查詢(xún)請(qǐng)求,正好讀取到該數(shù)據(jù),就會(huì)出現(xiàn)問(wèn)題,因?yàn)樵摂?shù)據(jù)在數(shù)據(jù)庫(kù)中根本不存在,這個(gè)問(wèn)題非常嚴(yán)重。

          我們都知道,緩存的主要目的是把數(shù)據(jù)庫(kù)的數(shù)據(jù)臨時(shí)保存在內(nèi)存,便于后續(xù)的查詢(xún),提升查詢(xún)速度。

          但如果某條數(shù)據(jù),在數(shù)據(jù)庫(kù)中都不存在,你緩存這種“假數(shù)據(jù)”又有啥意義呢?

          因此,先寫(xiě)緩存,再寫(xiě)數(shù)據(jù)庫(kù)的方案是不可取的,在實(shí)際工作中用得不多。

          3. 先寫(xiě)數(shù)據(jù)庫(kù),再寫(xiě)緩存

          既然上面的方案行不通,接下來(lái),聊聊先寫(xiě)數(shù)據(jù)庫(kù),再寫(xiě)緩存的方案,該方案在低并發(fā)編程中有人在用(我猜的)。用戶(hù)的寫(xiě)操作,先寫(xiě)數(shù)據(jù)庫(kù),再寫(xiě)緩存,可以避免之前“假數(shù)據(jù)”的問(wèn)題。但它卻帶來(lái)了新的問(wèn)題。

          什么問(wèn)題呢?

          3.1 寫(xiě)緩存失敗了

          如果把寫(xiě)數(shù)據(jù)庫(kù)和寫(xiě)緩存操作,放在同一個(gè)事務(wù)當(dāng)中,當(dāng)寫(xiě)緩存失敗了,我們可以把寫(xiě)入數(shù)據(jù)庫(kù)的數(shù)據(jù)進(jìn)行回滾。如果是并發(fā)量比較小,對(duì)接口性能要求不太高的系統(tǒng),可以這么玩。

          但如果在高并發(fā)的業(yè)務(wù)場(chǎng)景中,寫(xiě)數(shù)據(jù)庫(kù)和寫(xiě)緩存,都屬于遠(yuǎn)程操作。為了防止出現(xiàn)大事務(wù),造成的死鎖問(wèn)題,通常建議寫(xiě)數(shù)據(jù)庫(kù)和寫(xiě)緩存不要放在同一個(gè)事務(wù)中。

          也就是說(shuō)在該方案中,如果寫(xiě)數(shù)據(jù)庫(kù)成功了,但寫(xiě)緩存失敗了,數(shù)據(jù)庫(kù)中已寫(xiě)入的數(shù)據(jù)不會(huì)回滾。

          這就會(huì)出現(xiàn):數(shù)據(jù)庫(kù)是新數(shù)據(jù),而緩存是舊數(shù)據(jù),兩邊數(shù)據(jù)不一致的情況。

          3.1 高并發(fā)下的問(wèn)題

          假設(shè)在高并發(fā)的場(chǎng)景中,針對(duì)同一個(gè)用戶(hù)的同一條數(shù)據(jù),有兩個(gè)寫(xiě)數(shù)據(jù)請(qǐng)求:a和b,它們同時(shí)請(qǐng)求到業(yè)務(wù)系統(tǒng)。

          其中請(qǐng)求a獲取的是舊數(shù)據(jù),而請(qǐng)求b獲取的是新數(shù)據(jù),如下圖所示:

          1. 請(qǐng)求a先過(guò)來(lái),剛寫(xiě)完了數(shù)據(jù)庫(kù)。但由于網(wǎng)絡(luò)原因,卡頓了一下,還沒(méi)來(lái)得及寫(xiě)緩存。
          2. 這時(shí)候請(qǐng)求b過(guò)來(lái)了,先寫(xiě)了數(shù)據(jù)庫(kù)。
          3. 接下來(lái),請(qǐng)求b順利寫(xiě)了緩存。
          4. 此時(shí),請(qǐng)求a卡頓結(jié)束,也寫(xiě)了緩存。

          很顯然,在這個(gè)過(guò)程當(dāng)中,請(qǐng)求b在緩存中的新數(shù)據(jù),被請(qǐng)求a的舊數(shù)據(jù)覆蓋了。

          也就是說(shuō):在高并發(fā)場(chǎng)景中,如果多個(gè)線程同時(shí)執(zhí)行先寫(xiě)數(shù)據(jù)庫(kù),再寫(xiě)緩存的操作,可能會(huì)出現(xiàn)數(shù)據(jù)庫(kù)是新值,而緩存中是舊值,兩邊數(shù)據(jù)不一致的情況。

          3.2 浪費(fèi)系統(tǒng)資源

          該方案還有一個(gè)比較大的問(wèn)題就是:每個(gè)寫(xiě)操作,寫(xiě)完數(shù)據(jù)庫(kù),會(huì)馬上寫(xiě)緩存,比較浪費(fèi)系統(tǒng)資源

          為什么這么說(shuō)呢?

          你可以試想一下,如果寫(xiě)的緩存,并不是簡(jiǎn)單的數(shù)據(jù)內(nèi)容,而是要經(jīng)過(guò)非常復(fù)雜的計(jì)算得出的最終結(jié)果。這樣每寫(xiě)一次緩存,都需要經(jīng)過(guò)一次非常復(fù)雜的計(jì)算,不是非常浪費(fèi)系統(tǒng)資源嗎?

          尤其是cpu內(nèi)存資源。

          還有些業(yè)務(wù)場(chǎng)景比較特殊:寫(xiě)多讀少

          如果在這類(lèi)業(yè)務(wù)場(chǎng)景中,每個(gè)用的寫(xiě)操作,都需要寫(xiě)一次緩存,有點(diǎn)得不償失。

          由此可見(jiàn),在高并發(fā)的場(chǎng)景中,先寫(xiě)數(shù)據(jù)庫(kù),再寫(xiě)緩存,這套方案問(wèn)題挺多的,也不太建議使用。

          如果你已經(jīng)用了,趕緊看看踩坑了沒(méi)?

          4. 先刪緩存,再寫(xiě)數(shù)據(jù)庫(kù)

          通過(guò)上面的內(nèi)容我們得知,如果直接更新緩存的問(wèn)題很多。

          那么,為何我們不能換一種思路:不去直接更新緩存,而改為刪除緩存呢?

          刪除緩存方案,同樣有兩種:

          1. 先刪緩存,再寫(xiě)數(shù)據(jù)庫(kù)
          2. 先寫(xiě)數(shù)據(jù)庫(kù),再刪緩存

          我們一起先看看:先刪緩存,再寫(xiě)數(shù)據(jù)庫(kù)的情況。

          說(shuō)白了,在用戶(hù)的寫(xiě)操作中,先執(zhí)行刪除緩存操作,再去寫(xiě)數(shù)據(jù)庫(kù)。這套方案,可以是可以,但也會(huì)有一樣問(wèn)題。

          4.1 高并發(fā)下的問(wèn)題

          假設(shè)在高并發(fā)的場(chǎng)景中,同一個(gè)用戶(hù)的同一條數(shù)據(jù),有一個(gè)讀數(shù)據(jù)請(qǐng)求c,還有另一個(gè)寫(xiě)數(shù)據(jù)請(qǐng)求d(一個(gè)更新操作),同時(shí)請(qǐng)求到業(yè)務(wù)系統(tǒng)。如下圖所示:

          1. 請(qǐng)求d先過(guò)來(lái),把緩存刪除了。但由于網(wǎng)絡(luò)原因,卡頓了一下,還沒(méi)來(lái)得及寫(xiě)數(shù)據(jù)庫(kù)。
          2. 這時(shí)請(qǐng)求c過(guò)來(lái)了,先查緩存發(fā)現(xiàn)沒(méi)數(shù)據(jù),再查數(shù)據(jù)庫(kù),有數(shù)據(jù),但是舊值。
          3. 請(qǐng)求c將數(shù)據(jù)庫(kù)中的舊值,更新到緩存中。
          4. 此時(shí),請(qǐng)求d卡頓結(jié)束,把新值寫(xiě)入數(shù)據(jù)庫(kù)。

          在這個(gè)過(guò)程當(dāng)中,請(qǐng)求d的新值并沒(méi)有被請(qǐng)求c寫(xiě)入緩存,同樣會(huì)導(dǎo)致緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致的情況。更正:圖中步驟7寫(xiě)入舊值,步驟9要?jiǎng)h掉。

          那么,這種場(chǎng)景的數(shù)據(jù)不一致問(wèn)題,能否解決呢?

          4.2 緩存雙刪

          在上面的業(yè)務(wù)場(chǎng)景中,一個(gè)讀數(shù)據(jù)請(qǐng)求,一個(gè)寫(xiě)數(shù)據(jù)請(qǐng)求。當(dāng)寫(xiě)數(shù)據(jù)請(qǐng)求把緩存刪了之后,讀數(shù)據(jù)請(qǐng)求,可能把當(dāng)時(shí)從數(shù)據(jù)庫(kù)查詢(xún)出來(lái)的舊值,寫(xiě)入緩存當(dāng)中。

          有人說(shuō)還不好辦,請(qǐng)求d在寫(xiě)完數(shù)據(jù)庫(kù)之后,把緩存重新刪一次不就行了?這就是我們所說(shuō)的緩存雙刪,即在寫(xiě)數(shù)據(jù)庫(kù)之前刪除一次,寫(xiě)完數(shù)據(jù)庫(kù)后,再刪除一次。

          該方案有個(gè)非常關(guān)鍵的地方是:第二次刪除緩存,并非立馬就刪,而是要在一定的時(shí)間間隔之后。

          我們?cè)僦匦禄仡櫼幌拢卟l(fā)下一個(gè)讀數(shù)據(jù)請(qǐng)求,一個(gè)寫(xiě)數(shù)據(jù)請(qǐng)求導(dǎo)致數(shù)據(jù)不一致的產(chǎn)生過(guò)程:

          1. 請(qǐng)求d先過(guò)來(lái),把緩存刪除了。但由于網(wǎng)絡(luò)原因,卡頓了一下,還沒(méi)來(lái)得及寫(xiě)數(shù)據(jù)庫(kù)。
          2. 這時(shí)請(qǐng)求c過(guò)來(lái)了,先查緩存發(fā)現(xiàn)沒(méi)數(shù)據(jù),再查數(shù)據(jù)庫(kù),有數(shù)據(jù),但是舊值。
          3. 請(qǐng)求c將數(shù)據(jù)庫(kù)中的舊值,更新到緩存中。
          4. 此時(shí),請(qǐng)求d卡頓結(jié)束,把新值寫(xiě)入數(shù)據(jù)庫(kù)。
          5. 一段時(shí)間之后,比如:500ms,請(qǐng)求d將緩存刪除。

          這樣來(lái)看確實(shí)可以解決緩存不一致問(wèn)題。

          那么,為什么一定要間隔一段時(shí)間之后,才能刪除緩存呢?

          請(qǐng)求d卡頓結(jié)束,把新值寫(xiě)入數(shù)據(jù)庫(kù)后,請(qǐng)求c將數(shù)據(jù)庫(kù)中的舊值,更新到緩存中。

          此時(shí),如果請(qǐng)求d刪除太快,在請(qǐng)求c將數(shù)據(jù)庫(kù)中的舊值更新到緩存之前,就已經(jīng)把緩存刪除了,這次刪除就沒(méi)任何意義。必須要在請(qǐng)求c更新緩存之后,再刪除緩存,才能把舊值及時(shí)刪除了。

          所以需要在請(qǐng)求d中加一個(gè)時(shí)間間隔,確保請(qǐng)求c,或者類(lèi)似于請(qǐng)求c的其他請(qǐng)求,如果在緩存中設(shè)置了舊值,最終都能夠被請(qǐng)求d刪除掉。

          接下來(lái),還有一個(gè)問(wèn)題:如果第二次刪除緩存時(shí),刪除失敗了該怎么辦?

          這里先留點(diǎn)懸念,后面會(huì)詳細(xì)說(shuō)。

          5. 先寫(xiě)數(shù)據(jù)庫(kù),再刪緩存

          從前面得知,先刪緩存,再寫(xiě)數(shù)據(jù)庫(kù),在并發(fā)的情況下,也可能會(huì)出現(xiàn)緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致的情況。

          那么,我們只能寄希望于最后的方案了。

          接下來(lái),我們重點(diǎn)看看先寫(xiě)數(shù)據(jù)庫(kù),再刪緩存的方案。在高并發(fā)的場(chǎng)景中,有一個(gè)讀數(shù)據(jù)請(qǐng)求,有一個(gè)寫(xiě)數(shù)據(jù)請(qǐng)求,更新過(guò)程如下:

          1. 請(qǐng)求e先寫(xiě)數(shù)據(jù)庫(kù),由于網(wǎng)絡(luò)原因卡頓了一下,沒(méi)有來(lái)得及刪除緩存。
          2. 請(qǐng)求f查詢(xún)緩存,發(fā)現(xiàn)緩存中有數(shù)據(jù),直接返回該數(shù)據(jù)。
          3. 請(qǐng)求e刪除緩存。

          在這個(gè)過(guò)程中,只有請(qǐng)求f讀了一次舊數(shù)據(jù),后來(lái)舊數(shù)據(jù)被請(qǐng)求e及時(shí)刪除了,看起來(lái)問(wèn)題不大。

          但如果是讀數(shù)據(jù)請(qǐng)求先過(guò)來(lái)呢?

          1. 請(qǐng)求f查詢(xún)緩存,發(fā)現(xiàn)緩存中有數(shù)據(jù),直接返回該數(shù)據(jù)。
          2. 請(qǐng)求e先寫(xiě)數(shù)據(jù)庫(kù)。
          3. 請(qǐng)求e刪除緩存。

          這種情況看起來(lái)也沒(méi)問(wèn)題呀?

          答:對(duì)的。

          但就怕出現(xiàn)下面這種情況,即緩存自己失效了。如下圖所示:

          1. 緩存過(guò)期時(shí)間到了,自動(dòng)失效。
          2. 請(qǐng)求f查詢(xún)緩存,發(fā)緩存中沒(méi)有數(shù)據(jù),查詢(xún)數(shù)據(jù)庫(kù)的舊值,但由于網(wǎng)絡(luò)原因卡頓了,沒(méi)有來(lái)得及更新緩存。
          3. 請(qǐng)求e先寫(xiě)數(shù)據(jù)庫(kù),接著刪除了緩存。
          4. 請(qǐng)求f更新舊值到緩存中。

          這時(shí),緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)同樣出現(xiàn)不一致的情況了。

          但這種情況還是比較少的,需要同時(shí)滿(mǎn)足以下條件才可以:

          1. 緩存剛好自動(dòng)失效。
          2. 請(qǐng)求f從數(shù)據(jù)庫(kù)查出舊值,更新緩存的耗時(shí),比請(qǐng)求e寫(xiě)數(shù)據(jù)庫(kù),并且刪除緩存的還長(zhǎng)。

          我們都知道查詢(xún)數(shù)據(jù)庫(kù)的速度,一般比寫(xiě)數(shù)據(jù)庫(kù)要快,更何況寫(xiě)完數(shù)據(jù)庫(kù),還要?jiǎng)h除緩存。所以絕大多數(shù)情況下,寫(xiě)數(shù)據(jù)請(qǐng)求比讀數(shù)據(jù)情況耗時(shí)更長(zhǎng)。

          由此可見(jiàn),系統(tǒng)同時(shí)滿(mǎn)足上述兩個(gè)條件的概率非常小。

          推薦大家使用先寫(xiě)數(shù)據(jù)庫(kù),再刪緩存的方案,雖說(shuō)不能100%避免數(shù)據(jù)不一致問(wèn)題,但出現(xiàn)該問(wèn)題的概率,相對(duì)于其他方案來(lái)說(shuō)是最小的。

          但在該方案中,如果刪除緩存失敗了該怎么辦呢?

          6. 刪緩存失敗怎么辦?

          其實(shí)先寫(xiě)數(shù)據(jù)庫(kù),再刪緩存的方案,跟緩存雙刪的方案一樣,有一個(gè)共同的風(fēng)險(xiǎn)點(diǎn),即:如果緩存刪除失敗了,也會(huì)導(dǎo)致緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致。

          那么,刪除緩存失敗怎么辦呢?

          答:需要加重試機(jī)制

          在接口中如果更新了數(shù)據(jù)庫(kù)成功了,但更新緩存失敗了,可以立刻重試3次。如果其中有任何一次成功,則直接返回成功。如果3次都失敗了,則寫(xiě)入數(shù)據(jù)庫(kù),準(zhǔn)備后續(xù)再處理。

          當(dāng)然,如果你在接口中直接同步重試,該接口并發(fā)量比較高的時(shí)候,可能有點(diǎn)影響接口性能。

          這時(shí),就需要改成異步重試了。

          異步重試方式有很多種,比如:

          1. 每次都單獨(dú)起一個(gè)線程,該線程專(zhuān)門(mén)做重試的工作。但如果在高并發(fā)的場(chǎng)景下,可能會(huì)創(chuàng)建太多的線程,導(dǎo)致系統(tǒng)OOM問(wèn)題,不太建議使用。
          2. 將重試的任務(wù)交給線程池處理,但如果服務(wù)器重啟,部分?jǐn)?shù)據(jù)可能會(huì)丟失。
          3. 將重試數(shù)據(jù)寫(xiě)表,然后使用elastic-job等定時(shí)任務(wù)進(jìn)行重試。
          4. 將重試的請(qǐng)求寫(xiě)入mq等消息中間件中,在mq的consumer中處理。
          5. 訂閱mysql的binlog,在訂閱者中,如果發(fā)現(xiàn)了更新數(shù)據(jù)請(qǐng)求,則刪除相應(yīng)的緩存。

          7. 定時(shí)任務(wù)

          使用定時(shí)任務(wù)重試的具體方案如下:

          1. 當(dāng)用戶(hù)操作寫(xiě)完數(shù)據(jù)庫(kù),但刪除緩存失敗了,需要將用戶(hù)數(shù)據(jù)寫(xiě)入重試表中。如下圖所示:
          2. 在定時(shí)任務(wù)中,異步讀取重試表中的用戶(hù)數(shù)據(jù)。重試表需要記錄一個(gè)重試次數(shù)字段,初始值為0。然后重試5次,不斷刪除緩存,每重試一次該字段值+1。如果其中有任意一次成功了,則返回成功。如果重試了5次,還是失敗,則我們需要在重試表中記錄一個(gè)失敗的狀態(tài),等待后續(xù)進(jìn)一步處理。
          3. 在高并發(fā)場(chǎng)景中,定時(shí)任務(wù)推薦使用elastic-job。相對(duì)于xxl-job等定時(shí)任務(wù),它可以分片處理,提升處理速度。同時(shí)每片的間隔可以設(shè)置成:1,2,3,5,7秒等。

          如果大家對(duì)定時(shí)任務(wù)比較感興趣的話,可以看看我的另一篇文章《學(xué)會(huì)這10種定時(shí)任務(wù),我有點(diǎn)飄了》,里面列出了目前最主流的定時(shí)任務(wù)。

          使用定時(shí)任務(wù)重試的話,有個(gè)缺點(diǎn)就是實(shí)時(shí)性沒(méi)那么高,對(duì)于實(shí)時(shí)性要求特別高的業(yè)務(wù)場(chǎng)景,該方案不太適用。但是對(duì)于一般場(chǎng)景,還是可以用一用的。

          但它有一個(gè)很大的優(yōu)點(diǎn),即數(shù)據(jù)是落庫(kù)的,不會(huì)丟數(shù)據(jù)。

          8. mq

          在高并發(fā)的業(yè)務(wù)場(chǎng)景中,mq(消息隊(duì)列)是必不可少的技術(shù)之一。它不僅可以異步解耦,還能削峰填谷。對(duì)保證系統(tǒng)的穩(wěn)定性是非常有意義的。

          對(duì)mq有興趣的朋友可以看看我的另一篇文章《mq的那些破事兒》。

          mq的生產(chǎn)者,生產(chǎn)了消息之后,通過(guò)指定的topic發(fā)送到mq服務(wù)器。然后mq的消費(fèi)者,訂閱該topic的消息,讀取消息數(shù)據(jù)之后,做業(yè)務(wù)邏輯處理。

          使用mq重試的具體方案如下:

          1. 當(dāng)用戶(hù)操作寫(xiě)完數(shù)據(jù)庫(kù),但刪除緩存失敗了,產(chǎn)生一條mq消息,發(fā)送給mq服務(wù)器。
          2. mq消費(fèi)者讀取mq消息,重試5次刪除緩存。如果其中有任意一次成功了,則返回成功。如果重試了5次,還是失敗,則寫(xiě)入死信隊(duì)列中。
          3. 推薦mq使用rocketmq,重試機(jī)制和死信隊(duì)列默認(rèn)是支持的。使用起來(lái)非常方便,而且還支持順序消息,延遲消息和事務(wù)消息等多種業(yè)務(wù)場(chǎng)景。

          當(dāng)然在該方案中,刪除緩存可以完全走異步。即用戶(hù)的寫(xiě)操作,在寫(xiě)完數(shù)據(jù)庫(kù)之后,不用立刻刪除一次緩存。而直接發(fā)送mq消息,到mq服務(wù)器,然后有mq消費(fèi)者全權(quán)負(fù)責(zé)刪除緩存的任務(wù)。

          因?yàn)閙q的實(shí)時(shí)性還是比較高的,因此改良后的方案也是一種不錯(cuò)的選擇。

          9. binlog

          前面我們聊過(guò)的,無(wú)論是定時(shí)任務(wù),還是mq(消息隊(duì)列),做重試機(jī)制,對(duì)業(yè)務(wù)都有一定的侵入性。

          在使用定時(shí)任務(wù)的方案中,需要在業(yè)務(wù)代碼中增加額外邏輯,如果刪除緩存失敗,需要將數(shù)據(jù)寫(xiě)入重試表。

          而使用mq的方案中,如果刪除緩存失敗了,需要在業(yè)務(wù)代碼中發(fā)送mq消息到mq服務(wù)器。

          其實(shí),還有一種更優(yōu)雅的實(shí)現(xiàn),即監(jiān)聽(tīng)binlog,比如使用:canal等中間件。

          具體方案如下:

          1. 在業(yè)務(wù)接口中寫(xiě)數(shù)據(jù)庫(kù)之后,就不管了,直接返回成功。
          2. mysql服務(wù)器會(huì)自動(dòng)把變更的數(shù)據(jù)寫(xiě)入binlog中。
          3. binlog訂閱者獲取變更的數(shù)據(jù),然后刪除緩存。

          這套方案中業(yè)務(wù)接口確實(shí)簡(jiǎn)化了一些流程,只用關(guān)心數(shù)據(jù)庫(kù)操作即可,而在binlog訂閱者中做緩存刪除工作。

          但如果只是按照?qǐng)D中的方案進(jìn)行刪除緩存,只刪除了一次,也可能會(huì)失敗。

          如何解決這個(gè)問(wèn)題呢?

          答:這就需要加上前面聊過(guò)的重試機(jī)制了。如果刪除緩存失敗,寫(xiě)入重試表,使用定時(shí)任務(wù)重試。或者寫(xiě)入mq,讓mq自動(dòng)重試。

          在這里推薦使用mq自動(dòng)重試機(jī)制在binlog訂閱者中如果刪除緩存失敗,則發(fā)送一條mq消息到mq服務(wù)器,在mq消費(fèi)者中自動(dòng)重試5次。如果有任意一次成功,則直接返回成功。如果重試5次后還是失敗,則該消息自動(dòng)被放入死信隊(duì)列,后面可能需要人工介入。

          推薦閱讀:

          ?? 半年時(shí)間寫(xiě)個(gè)開(kāi)源框架?

          ???輕量級(jí)動(dòng)態(tài)線程池才是“王道”?


          最后說(shuō)一句(求關(guān)注,別白嫖我)

          如果這篇文章對(duì)您有所幫助,或者有所啟發(fā)的話,幫忙掃描下發(fā)二維碼關(guān)注一下,您的支持是我堅(jiān)持寫(xiě)作最大的動(dòng)力。

          求一鍵三連:點(diǎn)贊、轉(zhuǎn)發(fā)、在看。

          關(guān)注公眾號(hào):【蘇三說(shuō)技術(shù)】,在公眾號(hào)中回復(fù):面試、代碼神器、開(kāi)發(fā)手冊(cè)、時(shí)間管理有超贊的粉絲福利,另外回復(fù):加群,可以跟很多BAT大廠的前輩交流和學(xué)習(xí)。

          瀏覽 21
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  北条麻妃无码中文 | 丁香六月天 | 免费在线性爱视频 | 亚洲人妻在线观看 | 亚洲丰满少妇XXXXⅩ高潮 |