<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ù)的一致性很簡(jiǎn)單嗎?

          共 3815字,需瀏覽 8分鐘

           ·

          2020-10-10 21:09


          靈魂拷問(wèn)

          • 保證緩存和數(shù)據(jù)庫(kù)的一致性很簡(jiǎn)單嗎?
          • 有哪些方式能保證緩存和數(shù)據(jù)庫(kù)的一致性呢?
          • 如果發(fā)生了緩存和數(shù)據(jù)庫(kù)數(shù)據(jù)不一致的情況怎么辦呢?

          在上篇文章我們介紹了緩存的定義分類以及優(yōu)缺點(diǎn)等,如果還沒(méi)看的同學(xué)可以移步這里

          聽(tīng)說(shuō)你會(huì)緩存?

          當(dāng)我們的系統(tǒng)引入緩存組件之后,性能得到了大幅度提升,但是隨之而來(lái)的是代碼需要引入一定的復(fù)雜度,比如緩存的更新策略,寫入策略,過(guò)期策略等,而其中最可能導(dǎo)致程序員加班的莫過(guò)于緩存和數(shù)據(jù)庫(kù)的一致性問(wèn)題了,既:緩存中的數(shù)據(jù)和數(shù)據(jù)庫(kù)中的數(shù)據(jù)不一致。

          一致性問(wèn)題

          說(shuō)到一致性問(wèn)題,這算是分布式系統(tǒng)中不可避免的一個(gè)痛點(diǎn),或者說(shuō)分布式系統(tǒng)天然就自帶了數(shù)據(jù)一致性問(wèn)題,雖然可以利用很多分布式事務(wù)解決方案來(lái)做到一致性,但是實(shí)際的系統(tǒng)架構(gòu)設(shè)計(jì)中,我還是推崇避免分布式事務(wù)。緩存和數(shù)據(jù)庫(kù)數(shù)據(jù)的一致性在產(chǎn)生原理上和分布式類似,其實(shí)可以把他們兩個(gè)的關(guān)系看做是分布式系統(tǒng)中的兩個(gè)操作節(jié)點(diǎn)。

          凡是處于不同物理位置的兩個(gè)操作,如果操作的是相同數(shù)據(jù),都會(huì)遇到一致性問(wèn)題

          產(chǎn)生數(shù)據(jù)一致性問(wèn)題的根本原因是對(duì)一個(gè)數(shù)據(jù)的多個(gè)操作過(guò)程,緩存和數(shù)據(jù)庫(kù)數(shù)據(jù)的一致性也是這個(gè)原理,系統(tǒng)中最常見(jiàn)的操作流程是這樣的:

          • 數(shù)據(jù)的請(qǐng)求首先查詢緩存中是否存在該數(shù)據(jù)
          • 如果數(shù)據(jù)命中緩存(在緩存中存在)則直接返回?cái)?shù)據(jù),如果數(shù)據(jù)沒(méi)有命中緩存(緩存中不存在),則去數(shù)據(jù)庫(kù)中取數(shù)據(jù)
          • 從數(shù)據(jù)庫(kù)中取回?cái)?shù)據(jù),然后把數(shù)據(jù)寫入緩存
          好圖

          從圖中可以清楚的看到,對(duì)數(shù)據(jù)庫(kù)的操作和對(duì)緩存的操作是兩個(gè)不同階段的操作,在任何一個(gè)操作過(guò)程中都會(huì)發(fā)生線程安全問(wèn)題。比如說(shuō):

          • 當(dāng)兩個(gè)線程同時(shí)查詢緩存的時(shí)候,可能會(huì)發(fā)生兩個(gè)線程都沒(méi)有命中緩存的問(wèn)題
          • 如果兩個(gè)線程都沒(méi)有命中緩存就會(huì)發(fā)生同時(shí)查詢數(shù)據(jù)庫(kù)的問(wèn)題
          • 接著就會(huì)發(fā)生兩個(gè)線程同時(shí)回寫緩存的問(wèn)題

          而這還不是最致命的,畢竟兩個(gè)線程同時(shí)查詢數(shù)據(jù)庫(kù),同時(shí)回寫緩存數(shù)據(jù)在多數(shù)情況下緩存數(shù)據(jù)和數(shù)據(jù)庫(kù)數(shù)據(jù)還能保持一致。最要命的是如果是兩個(gè)線程都進(jìn)行更新操作,最常見(jiàn)的更新過(guò)程是先更新數(shù)據(jù)庫(kù),然后更新緩存。下面就以最常見(jiàn)的用戶積分場(chǎng)景為例,每個(gè)用戶都有自己的積分,假如發(fā)生以下過(guò)程:

          • 線程A根據(jù)業(yè)務(wù)會(huì)把用戶id為1的積分更新成100
          • 線程B根據(jù)業(yè)務(wù)會(huì)把用戶id為1的積分更新成200
          • 在數(shù)據(jù)庫(kù)層面,線程A和線程B肯定不存在并發(fā)情況,因?yàn)閿?shù)據(jù)庫(kù)用鎖來(lái)保證了ACID(假如是mysql等關(guān)系型數(shù)據(jù)庫(kù)),無(wú)論數(shù)據(jù)庫(kù)中最終的值是100還是200,我們都假設(shè)正確。
          • 假設(shè)線程B在A之后更新數(shù)據(jù)庫(kù),則數(shù)據(jù)庫(kù)中的值為200
          • 線程A和線程B在回寫緩存過(guò)程中,很可能會(huì)發(fā)生線程A在線程B之后操作緩存的情況(因?yàn)榫W(wǎng)絡(luò)調(diào)用存在不確定性),這個(gè)時(shí)候緩存內(nèi)的值會(huì)被更新成100,發(fā)生了緩存和數(shù)據(jù)庫(kù)不一致的情況

          通過(guò)以上案例可見(jiàn),解決緩存和數(shù)據(jù)庫(kù)數(shù)據(jù)不一致的根本解決方案是需要把兩個(gè)操作合并成邏輯上能保證事務(wù)的一個(gè)操作

          兩個(gè)操作看做一個(gè)操作

          分布式鎖

          在平時(shí)開(kāi)發(fā)中,利用分布式鎖可能算是比較常見(jiàn)的解決方案了。利用分布式鎖把緩存操作和數(shù)據(jù)庫(kù)操作封裝為邏輯上的一個(gè)操作可以保證數(shù)據(jù)的一致性,具體流程為:

          • 每個(gè)想要操作緩存和數(shù)據(jù)庫(kù)的線程都必須先申請(qǐng)分布式鎖
          • 如果成功獲得鎖,則進(jìn)行數(shù)據(jù)庫(kù)和緩存操作,操作完畢釋放鎖
          • 如果沒(méi)有獲得鎖,根據(jù)不同業(yè)務(wù)可以選擇阻塞等待或者輪訓(xùn),或者直接返回的策略
          image

          利用分布式鎖是解決分布式事務(wù)的一種方案,但是在一定程度上會(huì)降低系統(tǒng)的性能,而且分布式鎖的設(shè)計(jì)要考慮到down機(jī)和死鎖的意外情況,而最常見(jiàn)的分布式鎖就是利用redis,但是也會(huì)有不少坑,具體可以參考之前的文章

          redis做分布式鎖可能不那么簡(jiǎn)單

          刪除緩存

          相對(duì)于分布式鎖的方案,而程序員實(shí)際中最喜歡使用的還是刪除緩存的方式,在一個(gè)可能會(huì)發(fā)生不一致的場(chǎng)景下,我們會(huì)以數(shù)據(jù)庫(kù)為主,在操作完數(shù)據(jù)庫(kù)之后,不去更新緩存,而是刪除緩存。這在一定意義上相當(dāng)于只操作數(shù)據(jù)庫(kù),把需要維護(hù)的兩個(gè)數(shù)據(jù)源變成了一個(gè)數(shù)據(jù)源。

          image

          這種方式要求必須先操作數(shù)據(jù)庫(kù),后操作緩存,不然的話發(fā)生不一致的幾率會(huì)大很多。為什么這么說(shuō)呢?因?yàn)榫退闶窍炔僮鲾?shù)據(jù)庫(kù)也會(huì)有發(fā)生不一致的幾率,但是畢竟在整個(gè)操作過(guò)程中,刪除緩存的操作只占整個(gè)流程時(shí)間的一小部分而已,而且我們可以利用緩存的過(guò)期時(shí)間來(lái)保證數(shù)據(jù)的最終一致性,所以在一些可以容忍數(shù)據(jù)短暫不一致的場(chǎng)景下可以采用這種方案的。

          刪除緩存方案帶來(lái)的另外一個(gè)劣勢(shì)是:如果同樣的數(shù)據(jù)會(huì)被頻繁更新,緩存會(huì)被頻繁刪除,當(dāng)有讀請(qǐng)求的時(shí)候又會(huì)被頻繁的從數(shù)據(jù)庫(kù)加載,所以這種方案適用于那種對(duì)緩存命中率不敏感的系統(tǒng)中。

          單線程

          發(fā)生緩存和數(shù)據(jù)庫(kù)不一致的原因在于多個(gè)線程的同時(shí)操作,如果相同的數(shù)據(jù)始終只會(huì)有一個(gè)線程去操作,不一致的情況就會(huì)避免了,比如nodejs,可以充分利用nodejs單線程的優(yōu)勢(shì)。提到單線程不能不提一下Actor模型,actor模型在對(duì)于同樣的對(duì)象上可以看做是單線程模式,具體有興趣的同學(xué)可以查看之前的推文

          分布式高并發(fā)下Actor模型如此優(yōu)秀

          單線程的模式基本上和分布式鎖的方案類似,只不過(guò)單線程不需要鎖就可以實(shí)現(xiàn)操作的順序化,這也是單線程的優(yōu)勢(shì)所在。

          其他方案

          如果是以緩存為主呢?假如我們的應(yīng)用程序只和緩存組件通信,至于持久化數(shù)據(jù)庫(kù)由專門的程序負(fù)責(zé),這樣行不行呢?在理論上是可以的

          image

          不過(guò)這種方案需要考慮幾個(gè)方面:

          • 數(shù)據(jù)從緩存持久化到數(shù)據(jù)采用什么樣的解決方案,是同步進(jìn)行還是異步進(jìn)行呢?
          • 在新數(shù)據(jù)請(qǐng)求的時(shí)候,如果緩存不存在,要采用什么樣的方式來(lái)填充數(shù)據(jù)
          • 如果緩存模塊掛掉了該怎么辦?

          以緩存為主的方案的優(yōu)勢(shì)是數(shù)據(jù)優(yōu)先進(jìn)入IO速度快的設(shè)備,對(duì)于那些請(qǐng)求量大,但是可以容忍一定數(shù)據(jù)丟失的應(yīng)用非常合適,比如應(yīng)用log數(shù)據(jù)的收集系統(tǒng),這種系統(tǒng)其中一個(gè)最大的特點(diǎn)就是可以容忍一定數(shù)據(jù)的丟失,但是并發(fā)的請(qǐng)求數(shù)會(huì)非常大。所以我們就可以利用緩存設(shè)備前置的方案來(lái)應(yīng)對(duì)這種應(yīng)用場(chǎng)景

          回復(fù) 【關(guān)閉】學(xué)關(guān)
          回復(fù) 【實(shí)戰(zhàn)】獲取20套實(shí)戰(zhàn)源碼
          回復(fù) 【被刪】學(xué)個(gè)
          回復(fù) 【訪客】學(xué)
          回復(fù) 【小程序】學(xué)獲取15套【入門+實(shí)戰(zhàn)+賺錢】小程序源碼
          回復(fù) 【python】學(xué)微獲取全套0基礎(chǔ)Python知識(shí)手冊(cè)
          回復(fù) 【2019】獲取2019 .NET 開(kāi)發(fā)者峰會(huì)資料PPT
          回復(fù) 【加群】加入dotnet微信交流群

          一招搞定github下載速度到2MB/s


          微信突然更新,這些功能被禁用了


          瀏覽 49
          點(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>
                  国产wwwww | 久久久久久久大香蕉 | 久草视频在线免费播放 | 狠狠干在线观看 | 欧美高潮视频 |