<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>

          再有人問(wèn)你數(shù)據(jù)庫(kù)緩存一致性的問(wèn)題,直接把這篇文章發(fā)給他!

          共 3923字,需瀏覽 8分鐘

           ·

          2022-05-23 17:05

          作者 l Hollis
          來(lái)源 l Hollis(ID:hollischuang)


          在之前的一篇文章《為什么會(huì)出現(xiàn)數(shù)據(jù)庫(kù)和緩存不一致的問(wèn)題》中,我們介紹過(guò)緩存和數(shù)據(jù)庫(kù)會(huì)出現(xiàn)數(shù)據(jù)不一致的幾種情況。

          我們提到過(guò),在數(shù)據(jù)庫(kù)和緩存的操作過(guò)程中,可能存在”先寫(xiě)數(shù)據(jù)庫(kù),后刪緩存”、”先寫(xiě)數(shù)據(jù)庫(kù),后更新緩存”、”先刪緩存庫(kù),后寫(xiě)數(shù)據(jù)庫(kù)”以及”先更新緩存庫(kù),后寫(xiě)數(shù)據(jù)庫(kù)”這四種。

          那么,到底是應(yīng)該刪除緩存好呢,還是更新緩存好呢?到底應(yīng)該先操作數(shù)據(jù)庫(kù)呢還是先操作緩存呢?哪種方案更好呢?又該如何選擇呢?

          本文就來(lái)展開(kāi)分析一下。


          刪除還是更新

          為了保證數(shù)據(jù)庫(kù)和緩存里面的數(shù)據(jù)是一致的,很多人會(huì)很多人在做數(shù)據(jù)更新的時(shí)候,會(huì)同時(shí)更新緩存里面的內(nèi)容。但是我其實(shí)告訴大家,應(yīng)該優(yōu)先選擇刪除緩存而不是更新緩存。

          首先,我們暫時(shí)拋開(kāi)數(shù)據(jù)一致性的問(wèn)題,單獨(dú)來(lái)看看更新緩存和刪除緩存的復(fù)雜的的問(wèn)題。

          我們放到緩存中的數(shù)據(jù),很多時(shí)候可能不只是簡(jiǎn)單的一個(gè)字符串類(lèi)型的值,他還可能是一個(gè)大的JSON串,一個(gè)map類(lèi)型等等。

          舉個(gè)栗子,我們需要通過(guò)緩存進(jìn)行扣減庫(kù)存的時(shí)候,你可能需要從緩存中查出整個(gè)訂單模型數(shù)據(jù),把他進(jìn)行反序列化之后,再解析出其中的庫(kù)存字段,把他修改掉,然后再序列化,最后再更新到緩存中。

          可以看到,更新緩存的動(dòng)作,相比于直接刪除緩存,操作過(guò)程比較的復(fù)雜,而且也容易出錯(cuò)。

          還有就是,在數(shù)據(jù)庫(kù)和緩存的一致性保證方面,刪除緩存相比更新緩存要更簡(jiǎn)單一點(diǎn)。

          我們?cè)凇?/span>為什么會(huì)出現(xiàn)數(shù)據(jù)庫(kù)和緩存不一致的問(wèn)題》中介紹過(guò)的"寫(xiě)寫(xiě)并發(fā)"的場(chǎng)景中,如果同時(shí)更新緩存和數(shù)據(jù)庫(kù),那么很容易會(huì)出現(xiàn)因?yàn)椴l(fā)的問(wèn)題導(dǎo)致數(shù)據(jù)不一致的情況。如:

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


          先更新緩存,后寫(xiě)數(shù)據(jù)庫(kù):

          但是,如果是做緩存的刪除的話(huà),在寫(xiě)寫(xiě)并發(fā)的情況下,緩存中的數(shù)據(jù)都是要被清除的,所以就不會(huì)出現(xiàn)數(shù)據(jù)不一致的問(wèn)題。

          但是,更新緩存相比刪除緩存還是有一個(gè)小的缺點(diǎn),那就是帶來(lái)的一次額外的cache miss,也就是說(shuō)在刪除緩存后的下一次查詢(xún)會(huì)無(wú)法命中緩存,要查詢(xún)一下數(shù)據(jù)庫(kù)。

          這種cache miss在某種程度上可能會(huì)導(dǎo)致緩存擊穿,也就是剛好緩存被刪除之后,同一個(gè)Key有大量的請(qǐng)求過(guò)來(lái),導(dǎo)致緩存被擊穿,大量請(qǐng)求訪(fǎng)問(wèn)到數(shù)據(jù)庫(kù)。

          但是,通過(guò)加鎖的方式是可以比較方便的解決緩存擊穿的問(wèn)題的。

          總之,刪除緩存相比較更新緩存,方案更加簡(jiǎn)單,而且?guī)?lái)的一致性問(wèn)題也更少。所以,在刪除和更新緩存之間,我還是偏向于建議大家優(yōu)先選擇刪除緩存。


          刪除還是更新


          在確定了優(yōu)先選擇刪除緩存而不是更新緩存之后,留給我們的數(shù)據(jù)庫(kù)+緩存更新的可選方案就剩下:"先寫(xiě)數(shù)據(jù)庫(kù)后刪除緩存"和"先刪除緩存后寫(xiě)數(shù)據(jù)庫(kù)了"。

          那么,這兩種方式各自有什么優(yōu)缺點(diǎn)呢?該如何選擇呢?


          先寫(xiě)數(shù)據(jù)庫(kù)
          因?yàn)閿?shù)據(jù)庫(kù)和緩存的操作是兩步的,沒(méi)辦法做到保證原子性,所以就有可能第一步成功而第二步失敗。

          而一般情況下,如果把緩存的刪除動(dòng)作放到第二步,有一個(gè)好處,那就是緩存刪除失敗的概率還是比較低的,除非是網(wǎng)絡(luò)問(wèn)題或者緩存服務(wù)器宕機(jī)的問(wèn)題,否則大部分情況都是可以成功的。

          還有就是,先寫(xiě)數(shù)據(jù)庫(kù)后刪除緩存雖然不存在"寫(xiě)寫(xiě)并發(fā)"導(dǎo)致的數(shù)據(jù)一致性問(wèn)題,但是會(huì)存在"讀寫(xiě)并發(fā)"情況下的數(shù)據(jù)一致性問(wèn)題。

          我們知道,當(dāng)我們使用了緩存之后,一個(gè)讀的線(xiàn)程在查詢(xún)數(shù)據(jù)的過(guò)程是這樣的:

          1、查詢(xún)緩存,如果緩存中有值,則直接返回
          2、查詢(xún)數(shù)據(jù)庫(kù)
          3、把數(shù)據(jù)庫(kù)的查詢(xún)結(jié)果更新到緩存中

          所以,對(duì)于一個(gè)讀線(xiàn)程來(lái)說(shuō),雖然不會(huì)寫(xiě)數(shù)據(jù)庫(kù),但是是會(huì)更新緩存的,所以,在一些特殊的并發(fā)場(chǎng)景中,就會(huì)導(dǎo)致數(shù)據(jù)不一致的情況。

          讀寫(xiě)并發(fā)的時(shí)序如下:


          也就是說(shuō),假如一個(gè)讀線(xiàn)程,在讀緩存的時(shí)候沒(méi)查到值,他就會(huì)去數(shù)據(jù)庫(kù)中查詢(xún),但是如果自查詢(xún)到結(jié)果之后,更新緩存之前,數(shù)據(jù)庫(kù)被更新了,但是這個(gè)讀線(xiàn)程是完全不知道的,那么就導(dǎo)致最終緩存會(huì)被重新用一個(gè)"舊值"覆蓋掉。

          這也就導(dǎo)致了緩存和數(shù)據(jù)庫(kù)的不一致的現(xiàn)象。

          但是這種現(xiàn)象其實(shí)發(fā)生的概率比較低,因?yàn)橐话阋粋€(gè)讀操作是很快的,數(shù)據(jù)庫(kù)+緩存的讀操作基本在十幾毫秒左右就可以完成了。

          而在這期間,更好另一個(gè)線(xiàn)程執(zhí)行了一個(gè)比較耗時(shí)的寫(xiě)操作的概率確實(shí)比較低。

          先刪緩存

          那么,如果是先刪除緩存后操作數(shù)據(jù)庫(kù)的話(huà),會(huì)不會(huì)方案更完美一點(diǎn)呢?

          首先,如果是選擇先刪除緩存后寫(xiě)數(shù)據(jù)庫(kù)的這種方案,那么第二步的失敗是可以接受的,因?yàn)檫@樣不會(huì)有臟數(shù)據(jù),也沒(méi)什么影響,只需要重試就好了。

          但是,先刪除緩存后寫(xiě)數(shù)據(jù)庫(kù)的這種方式,會(huì)無(wú)形中放大前面我們提到的"讀寫(xiě)并發(fā)"導(dǎo)致的數(shù)據(jù)不一致的問(wèn)題。

          因?yàn)檫@種"讀寫(xiě)并發(fā)"問(wèn)題發(fā)生的前提是讀線(xiàn)程讀緩存沒(méi)讀到值,而先刪緩存的動(dòng)作一旦發(fā)生,剛好可以讓讀線(xiàn)程就從緩存中讀不到值。

          所以,本來(lái)一個(gè)小概率會(huì)發(fā)生的"讀寫(xiě)并發(fā)"問(wèn)題,在先刪緩存的過(guò)程中,問(wèn)題發(fā)生的概率會(huì)被放大。

          而且這種問(wèn)題的后果也比較嚴(yán)重,那就是緩存中的值一直是錯(cuò)的,就會(huì)導(dǎo)致后續(xù)的所以命中緩存的查詢(xún)結(jié)果都是錯(cuò)的!


          延遲雙刪

          那么,雖然先寫(xiě)數(shù)據(jù)后刪除緩存的這種情況,可以大大的降低并發(fā)問(wèn)題的概率,但是,根據(jù)墨菲定律,只要有可能發(fā)生的壞事,那就基本上會(huì)發(fā)生。越是龐大的系統(tǒng)發(fā)生的概率越高。

          那么,有沒(méi)有什么辦法可以來(lái)解決一下這種情況帶來(lái)的不一致的問(wèn)題呢?

          其實(shí)是有一個(gè)比較常見(jiàn)的方案的,在很多公司內(nèi)用的也比較多,那就是延遲雙刪。

          因?yàn)?讀寫(xiě)并發(fā)"的問(wèn)題會(huì)導(dǎo)致并發(fā)發(fā)生后,緩存中的數(shù)被讀線(xiàn)程寫(xiě)進(jìn)去臟數(shù)據(jù),那么就只需要在寫(xiě)線(xiàn)程在寫(xiě)數(shù)據(jù)庫(kù)、刪緩存之后,延遲一段時(shí)間,在執(zhí)行一把刪除動(dòng)作就行了。

          這樣就能保證緩存中的臟數(shù)據(jù)被清理掉,避免后續(xù)的讀操作都讀到臟數(shù)據(jù)。當(dāng)然,這個(gè)延遲的時(shí)長(zhǎng)也很講究,到底多久來(lái)刪除呢?一般建議設(shè)置1-2s就可以了。

          當(dāng)然,這種方案也是有一個(gè)弊端的,那就是可能會(huì)導(dǎo)致緩存中準(zhǔn)確的數(shù)據(jù)被刪除掉。當(dāng)然這也問(wèn)題不大,就像我們前面說(shuō)過(guò)的,只是增加一次cache miss罷了



          如何選擇

          前面介紹了幾種情況的具體問(wèn)題和解決方案,那么實(shí)際工作中應(yīng)該如何選擇呢?

          我覺(jué)得主要還是根據(jù)實(shí)際的業(yè)務(wù)情況來(lái)分析。

          比如,如果業(yè)務(wù)量不大,并發(fā)不高的情況,可以選擇先刪除緩存,后更新數(shù)據(jù)庫(kù)的方式,因?yàn)檫@種方案更加簡(jiǎn)單。

          但是,如果是業(yè)務(wù)量比較大,并發(fā)度很高的話(huà),那么建議選擇先更新數(shù)據(jù)庫(kù),后刪除緩存的方式,因?yàn)檫@種方式并發(fā)問(wèn)題更少一些。但是可能會(huì)引入加鎖、延遲雙刪等更多機(jī)制,使得整個(gè)方案會(huì)更加復(fù)雜。

          其實(shí),先操作數(shù)據(jù)庫(kù),后操作緩存,是一種比較典型的設(shè)計(jì)模式——Cache Aside Pattern。

          這種模式的主要方案就是先寫(xiě)數(shù)據(jù)庫(kù),后刪緩存,而且緩存的刪除是可以在旁路異步執(zhí)行的。

          這種模式的優(yōu)點(diǎn)就是我們說(shuō)的,他可以解決"寫(xiě)寫(xiě)并發(fā)"導(dǎo)致的數(shù)據(jù)不一致問(wèn)題,并且可以大大降低"讀寫(xiě)并發(fā)"的問(wèn)題,所以這也是Facebook比較推崇的一種模式。



          優(yōu)化方案


          Cache Aside Pattern 這種模式中,我們可以異步的在旁路處理緩存。其實(shí)這種方案在大廠中確實(shí)有的還蠻多的。

          主要的方式就是借助數(shù)據(jù)庫(kù)的binlog或者基于異步消息訂閱的方式。

          也就是說(shuō),在代碼的主要邏輯中,先操作數(shù)據(jù)庫(kù)就行了,然后數(shù)據(jù)庫(kù)操作完,可以發(fā)一個(gè)異步消息出來(lái)。

          然后再由一個(gè)監(jiān)聽(tīng)者在接到消息之后,異步的把緩存中的數(shù)據(jù)刪除掉。

          或者干脆借助數(shù)據(jù)庫(kù)的binlog,訂閱到數(shù)據(jù)庫(kù)變更之后,異步的清除緩存。

          這兩種方式都會(huì)有一定的延時(shí),通常在毫秒級(jí)別,一般用于在可接受秒級(jí)延遲的業(yè)務(wù)場(chǎng)景中。



          設(shè)計(jì)模式


          前面介紹過(guò)了Cache Aside Pattern這種關(guān)于緩存操作的設(shè)計(jì)模式,那么其實(shí)還有幾種其他的設(shè)計(jì)模式,也一起展開(kāi)介紹一下:

          Read/Write Through Pattern

          在這兩種模式中,應(yīng)用程序?qū)⒕彺孀鳛橹饕臄?shù)據(jù)源,不需要感知數(shù)據(jù)庫(kù),更新數(shù)據(jù)庫(kù)和從數(shù)據(jù)庫(kù)的讀取的任務(wù)都交給緩存來(lái)代理。

          Read Through模式下,是由緩存配置一個(gè)讀模塊,它知道如何將數(shù)據(jù)庫(kù)中的數(shù)據(jù)寫(xiě)入緩存。在數(shù)據(jù)被請(qǐng)求的時(shí)候,如果未命中,則將數(shù)據(jù)從數(shù)據(jù)庫(kù)載入緩存。

          Write Through模式下,緩存配置一個(gè)寫(xiě)模塊,它知道如何將數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)。當(dāng)應(yīng)用要寫(xiě)入數(shù)據(jù)時(shí),緩存會(huì)先存儲(chǔ)數(shù)據(jù),并調(diào)用寫(xiě)模塊將數(shù)據(jù)寫(xiě)入數(shù)據(jù)庫(kù)。

          也就是說(shuō),這兩種模式下,不需要應(yīng)用自己去操作數(shù)據(jù)庫(kù),緩存自己就把活干完了。

          Write Behind Caching Pattern

          這種模式就是在更新數(shù)據(jù)的時(shí)候,只更新緩存,而不更新數(shù)據(jù)庫(kù),然后再異步的定時(shí)把緩存中的數(shù)據(jù)持久化到數(shù)據(jù)庫(kù)中。

          這種模式的優(yōu)缺點(diǎn)比較明顯,那就是讀寫(xiě)速度都很快,但是會(huì)造成一定的數(shù)據(jù)丟失。

          這種比較適合用在比如統(tǒng)計(jì)文章的訪(fǎng)問(wèn)量、點(diǎn)贊等場(chǎng)景中,允許數(shù)據(jù)少量丟失,但是速度要快。



          沒(méi)有銀彈

          人月神話(huà)》的作者Fred Brooks在早年有一篇很著名文章《No Silver Bullet》 ,他提到:

          在軟件開(kāi)發(fā)過(guò)程里是沒(méi)有萬(wàn)能的終殺性武器的,只有各種方法綜合運(yùn)用,才是解決之道。而各種聲稱(chēng)如何如何神奇的理論或方法,都不是能殺死“軟件危機(jī)”這頭人狼的銀彈。

          也就是說(shuō),沒(méi)有哪種技術(shù)手段或者方案,是放之四海皆準(zhǔn)的。如果有的話(huà),我們這些工程師也就沒(méi)有存在的必要了。

          所以,任何的技術(shù)方案,都是一個(gè)權(quán)衡的過(guò)程,要權(quán)衡的問(wèn)題有很多,業(yè)務(wù)的具體情況,實(shí)現(xiàn)的復(fù)雜度、實(shí)現(xiàn)的成本,團(tuán)隊(duì)成員的接受度、可維護(hù)性、容易理解的程度等等。

          所以,沒(méi)有一個(gè)"完美"的方案,只有"適合"的方案。

          但是,如何能選出一個(gè)適合的方案,這里面就需要有很多的輸入來(lái)做支撐了。希望本文的內(nèi)容可以為你日后的決策提供一點(diǎn)參考!


          歡迎添加小編微信,進(jìn)入交流群


          推薦閱讀:


          瀏覽 35
          點(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>
                  欧美精品第一区 | 欧美一区电影 | caobi视频 | 亚洲黄在线观看 | 欧美操逼网页 |