成人国产在线小视频_日韩寡妇人妻调教在线播放_色成人www永久在线观看_2018国产精品久久_亚洲欧美高清在线30p_亚洲少妇综合一区_黄色在线播放国产_亚洲另类技巧小说校园_国产主播xx日韩_a级毛片在线免费

資訊專(zhuān)欄INFORMATION COLUMN

分布式系統(tǒng)關(guān)注點(diǎn)——先寫(xiě)DB還是「緩存」?

ccj659 / 1433人閱讀

摘要:首當(dāng)其沖的就是先寫(xiě)還是緩存。先寫(xiě)還是緩存一個(gè)程序可以沒(méi)有緩存,但是一定要有數(shù)據(jù)庫(kù)。為了便于記憶,你可以和分布式系統(tǒng)的定理同時(shí)記憶,叫緩存的模式。否則引入分布式緩存的作用就小了很多。就是設(shè)置緩存定時(shí)過(guò)期或者定時(shí)往下游的分布式緩存拉取最新數(shù)據(jù)。

如果第二次看到我的文章,歡迎文末掃碼訂閱我個(gè)人的公眾號(hào)(跨界架構(gòu)師)喲~  
本文長(zhǎng)度為4209字,建議閱讀12分鐘。
堅(jiān)持原創(chuàng),每一篇都是用心之作~


在前一篇《360°全方位解讀「緩存」》中,我們聊了運(yùn)用緩存的三種思路,以及在一個(gè)完整的系統(tǒng)中可以設(shè)立緩存的幾個(gè)位置,并且分享了關(guān)于瀏覽器緩存、CDN緩存、網(wǎng)關(guān)(代理)緩存的一些使用經(jīng)驗(yàn)。

這次Z哥將深入到實(shí)際場(chǎng)景中,來(lái)看一下「進(jìn)程內(nèi)緩存」、「進(jìn)程外緩存」運(yùn)用時(shí)的一些最佳實(shí)踐。由于篇幅原因,這次先聊三個(gè)問(wèn)題。

首當(dāng)其沖的就是“先寫(xiě)DB還是緩存?”。我想,只要你開(kāi)始運(yùn)用緩存,這會(huì)是你第一個(gè)要好好思考的問(wèn)題,否則在前方等待你的就是災(zāi)難。。。


先寫(xiě)DB還是緩存?

一個(gè)程序可以沒(méi)有緩存,但是一定要有數(shù)據(jù)庫(kù)。這是大家的普遍觀點(diǎn),所以數(shù)據(jù)庫(kù)的重要性在你的潛意識(shí)里總是被放在了第一位。

先DB再緩存

如果不細(xì)想的話你可能會(huì)覺(jué)得,數(shù)據(jù)庫(kù)操作失敗了,自然緩存也不用操作了;數(shù)據(jù)庫(kù)操作成功了,再操作緩存,沒(méi)毛病。

但是數(shù)據(jù)庫(kù)操作成功,緩存操作的失敗的情況該怎么解?(主要在用到redis,memcached這種進(jìn)程外緩存的時(shí)候,由于網(wǎng)絡(luò)因素,失敗的可能性大增)

辦法也是有的,在操作數(shù)據(jù)庫(kù)的時(shí)候帶一個(gè)事務(wù),如果緩存操作失敗則事務(wù)回滾。大致的代碼意思如下:

begin trans
    var isDbSuccess = write db;
    if(isDbSuccess){
        var isCacheSuccess = write cache;
        if(isCacheSuccess){
            return success;
        }
        else{
            rollback db;
            return fail;
        }
    }
    else{
        return fail;
    }
    catch(Exception ex){
        rollback db;
    }
end trans


如此一來(lái)就萬(wàn)無(wú)一失了嗎?并不是。除了由于事務(wù)的引入,增加了數(shù)據(jù)庫(kù)的壓力之外,在極端場(chǎng)景下可能會(huì)出現(xiàn)rollback db失敗的情況。是不是很頭疼?

解決這個(gè)問(wèn)題的方式就是write cache的時(shí)候做delete操作,而不是set操作。如此一來(lái),用多一次cache miss的代價(jià)來(lái)?yè)Qrollback db失敗的問(wèn)題。

就像圖上所示,哪怕rollback失敗了,通過(guò)一次cache miss重新從db中載入舊值。

題外話:其實(shí)這種做法有一種專(zhuān)業(yè)的叫法——Cache Aside Pattern。為了便于記憶,你可以和分布式系統(tǒng)的CAP定理同時(shí)記憶,叫「緩存的CAP模式」。


是不是看上去妥了?可以開(kāi)始瀟灑了?

▲圖片來(lái)源于網(wǎng)絡(luò),版權(quán)歸原作者所有

如果你的數(shù)據(jù)庫(kù)沒(méi)有做高可用的話,的確可以妥了。但是如果數(shù)據(jù)庫(kù)做了高可用,就會(huì)涉及到主從數(shù)據(jù)庫(kù)的數(shù)據(jù)同步,這就有新問(wèn)題了

題外話:所以大家不要過(guò)度追求技術(shù)的酷炫,可能會(huì)得不償失,自找麻煩。


什么問(wèn)題呢?就是如果在數(shù)據(jù)還未同步到「從庫(kù)」的時(shí)候,由于cache miss去「從庫(kù)」取到了未同步前的舊值。


解決它的第一個(gè)方式很簡(jiǎn)單,也很粗暴。就是定時(shí)去「從庫(kù)」讀數(shù)據(jù),發(fā)現(xiàn)數(shù)據(jù)和緩存不一樣了就set到緩存里去。


但是這個(gè)方式有點(diǎn)“治標(biāo)不治本”。不斷的從數(shù)據(jù)庫(kù)定時(shí)讀取,對(duì)資源的消耗大不說(shuō),這個(gè)間隔頻率也不好定義一個(gè)比較合適的統(tǒng)一標(biāo)準(zhǔn),太短吧,會(huì)導(dǎo)致重復(fù)讀取的次數(shù)加大,太長(zhǎng)吧,又會(huì)導(dǎo)致緩存和數(shù)據(jù)庫(kù)不一致的時(shí)間變長(zhǎng)。

所以這個(gè)方案僅適用于項(xiàng)目中只有2、3處需要做這種處理的場(chǎng)景,并且還不能是數(shù)據(jù)會(huì)頻繁修改的情況。因?yàn)樵跀?shù)據(jù)修改頻次較高的場(chǎng)景,甚至可能還會(huì)出現(xiàn)這個(gè)定時(shí)機(jī)制所消耗的資源反而大于主程序的情況。


一般情況下,另一種更普適性的方案是采用接下去聊的這種更底層的方式進(jìn)行,就是“哪里有問(wèn)題處理哪里”,當(dāng)「從庫(kù)」完成同步的時(shí)候再額外做一次delete cache或者set cache的操作。

如此,雖說(shuō)也沒(méi)有100%解決短暫的數(shù)據(jù)不一致問(wèn)題,但是已經(jīng)將臟數(shù)據(jù)所存在的時(shí)長(zhǎng)降到了最低(最終由主從同步的耗時(shí)決定),并且大大減少了無(wú)謂的資源消耗。


可能你會(huì)說(shuō),“不行,這么一點(diǎn)時(shí)間也不能忍”怎么辦?辦法是有,但是會(huì)增加「主庫(kù)」的壓力。就是在產(chǎn)生數(shù)據(jù)庫(kù)寫(xiě)入動(dòng)作后的一小段時(shí)間內(nèi)強(qiáng)制讀「主庫(kù)」來(lái)加載緩存。

怎么實(shí)現(xiàn)呢?先得依賴一個(gè)共享存儲(chǔ),可以借助數(shù)據(jù)庫(kù)或者也可以是我們現(xiàn)在正在聊的分布式緩存。

然后,你在事務(wù)提交之后往共享存儲(chǔ)中臨時(shí)存一個(gè){ key = dbname + tablename + id,value = null,expire = 3s }這樣的數(shù)據(jù),并且再做一次delete cache的操作。

begin trans
    var isDbSuccess = write db;
    if(isDbSuccess){        
        var isCacheSuccess = delete cache;
        if(isCacheSuccess){
            return success;
        }
        else{
            rollback db;
            return fail;
        }
    }
    else{
        return fail;
    }
    catch(Exception ex){
        rollback db;
    }
end trans
?
//在這里做這個(gè)臨時(shí)存儲(chǔ),{key,value,expire}。
delete cache;

如此一來(lái),當(dāng)「讀數(shù)據(jù)」的時(shí)候發(fā)生cache miss,先判斷是否存在這個(gè)臨時(shí)數(shù)據(jù),只要在3秒內(nèi)就會(huì)強(qiáng)制走「主庫(kù)」取數(shù)據(jù)。


可以看到,不同的方案各有利弊,需要根據(jù)具體的場(chǎng)景仔細(xì)權(quán)衡。


先緩存再DB

你工作中的大部分場(chǎng)景對(duì)數(shù)據(jù)準(zhǔn)確性肯定是低容忍的,所以一般不建議選擇「先緩存再DB」的方案,因?yàn)閮?nèi)存是易失性的。一旦遇到操作緩存成功,操作DB失敗的情況,問(wèn)題就來(lái)了。

在這個(gè)時(shí)候最新的數(shù)據(jù)只有緩存里有,怎么辦?多帶帶起個(gè)線程不斷的重試往數(shù)據(jù)庫(kù)寫(xiě)?這個(gè)方案在一定程度上可行,但不適合用于對(duì)數(shù)據(jù)準(zhǔn)確性有高要求的場(chǎng)景,因?yàn)榫彺嬉坏炝耍瑪?shù)據(jù)就丟了!

題外話:哪怕選擇了這個(gè)方案,重試線程應(yīng)確保只有1個(gè),否則會(huì)存在“ABBA”的「并發(fā)寫(xiě)」問(wèn)題。


可能你會(huì)說(shuō)用delete cache不就沒(méi)問(wèn)題了?

可以是可以,但是要有個(gè)前提條件,訪問(wèn)緩存的程序不會(huì)產(chǎn)生并發(fā)。因?yàn)橹灰愕某绦蚴嵌嗑€程運(yùn)行的,一旦出現(xiàn)并發(fā)就有可能出現(xiàn)「讀」的線程由于cache miss從數(shù)據(jù)庫(kù)取的時(shí)候,「寫(xiě)」的線程還沒(méi)將數(shù)據(jù)寫(xiě)到數(shù)據(jù)庫(kù)的情況。

所以,哪怕用delete cache的方式,要么帶lock(多客戶端情況下還得上分布式鎖),要么必然出現(xiàn)數(shù)據(jù)不一致。


值得注意的是,如果數(shù)據(jù)庫(kù)同樣做了高可用,哪怕帶了lock,也還需要考慮和上面提到的「先DB再緩存」中一樣的由于主從同步的時(shí)間差可能會(huì)產(chǎn)生的問(wèn)題。

當(dāng)然了,「先緩存再DB」也不是一文不值。當(dāng)對(duì)寫(xiě)入速度有極致要求,而對(duì)數(shù)據(jù)準(zhǔn)確性沒(méi)那么高要求的場(chǎng)景下就非常好使,其實(shí)就是前一篇(《360°全方位解讀「緩存」》)提到的「延遲寫(xiě)」機(jī)制。


小結(jié)一下,相比緩存來(lái)說(shuō),數(shù)據(jù)庫(kù)的「高可用」一般會(huì)在系統(tǒng)發(fā)展的后期才會(huì)引入,所以在沒(méi)有引入數(shù)據(jù)庫(kù)「高可用」的情況下,Z哥建議你使用「先DB再緩存」的方式,并且緩存操作用delete而不是set,這樣基本就可以高枕無(wú)憂了。

但是如果數(shù)據(jù)庫(kù)做了「高可用」,那么團(tuán)隊(duì)必然也形成一定規(guī)模了,這個(gè)時(shí)候就老老實(shí)實(shí)的做數(shù)據(jù)庫(kù)變更記錄(binlog)的訂閱吧。


到這里可能有的小伙伴要問(wèn)了,“如果上了分布式緩存,還需要本地緩存嗎?”。


本地緩存還要不要?

在解答這個(gè)問(wèn)題之前我們先來(lái)思考一個(gè)問(wèn)題,一個(gè)分布式系統(tǒng)最重要的價(jià)值是什么?

是「無(wú)限擴(kuò)展」,只要堆硬件就能應(yīng)對(duì)業(yè)務(wù)增長(zhǎng)。要達(dá)到這點(diǎn)的背后需要滿足一個(gè)特性,就是程序要「無(wú)狀態(tài)」。那么既想引入緩存來(lái)加速,又要達(dá)到「無(wú)狀態(tài)」,靠的就是分布式緩存。

所以,能用分布式緩存解決的問(wèn)題就盡量不要引入本地緩存。否則引入分布式緩存的作用就小了很多。


但是在少數(shù)場(chǎng)景下,本地緩存還是可以發(fā)揮其價(jià)值的,但是我們需要仔細(xì)識(shí)別出來(lái)。主要是三個(gè)場(chǎng)景:

不經(jīng)常變更的數(shù)據(jù)。(比如一天甚至好幾天更新一次的那種)

需要支撐非常高的并發(fā)。(比如秒殺)

對(duì)數(shù)據(jù)準(zhǔn)確性能容忍的場(chǎng)景。(比如瀏覽量,評(píng)論數(shù)等)

不過(guò),我還是建議你,除了第二種場(chǎng)景,否則還是盡量不要引入本地緩存。原因我們下面來(lái)說(shuō)說(shuō)。


其實(shí)這個(gè)原因的根本問(wèn)題就是在引入了本地緩存后,本地緩存(進(jìn)程內(nèi)緩存)、分布式緩存(進(jìn)程外緩存)、數(shù)據(jù)庫(kù)這三者之間的數(shù)據(jù)一致性該怎么進(jìn)行呢?


本地緩存、分布式緩存、db之間的數(shù)據(jù)一致性

如果是個(gè)單點(diǎn)應(yīng)用程序的話,很簡(jiǎn)單,將本地緩存的操作放在最后就好了。

可能你會(huì)說(shuō)本地緩存修改失敗怎么辦?比如重復(fù)key啊什么的異常。那你可以反思一下為這種數(shù)據(jù)為什么可以成功的寫(xiě)進(jìn)數(shù)據(jù)庫(kù)。。。


但是,本地緩存帶來(lái)的一個(gè)巨大問(wèn)題就是:雖然一個(gè)節(jié)點(diǎn)沒(méi)問(wèn)題,但是多個(gè)本地緩存節(jié)點(diǎn)之間的數(shù)據(jù)如何同步?

解決這個(gè)問(wèn)題的方式中有兩種和之前我們聊過(guò)的Session問(wèn)題(《做了「負(fù)載均衡」就可以隨便加機(jī)器了嗎?》)是類(lèi)似的。要么是由接收修改的節(jié)點(diǎn)通知其它節(jié)點(diǎn)變更(通過(guò)rpc或者mq皆可),要么借助一致性hash讓同一個(gè)來(lái)源的請(qǐng)求固定落到一個(gè)節(jié)點(diǎn)上。后者可以讓不同節(jié)點(diǎn)上的本地緩存數(shù)據(jù)都不重復(fù),從源頭上避免了這個(gè)問(wèn)題。

但是這兩個(gè)方案走的都是極端,前者變更成本太高,比如需要通知上千個(gè)節(jié)點(diǎn)的話,這個(gè)成本難以接受。而后者的話對(duì)資源的消耗太高,而且還容易出現(xiàn)壓力分?jǐn)偛痪鶆虻膯?wèn)題。所以,一般系統(tǒng)規(guī)模小的時(shí)候可以考慮前者,而規(guī)模越大越會(huì)選擇后者。

還有一種相對(duì)中庸一些的,以降低數(shù)據(jù)的準(zhǔn)確性來(lái)?yè)Q成本的方案。就是設(shè)置緩存定時(shí)過(guò)期或者定時(shí)往下游的分布式緩存拉取最新數(shù)據(jù)。這和前面「先DB再緩存」中提到的定時(shí)機(jī)制是一樣的邏輯,勝在簡(jiǎn)單,缺點(diǎn)就是會(huì)存在更長(zhǎng)時(shí)間的數(shù)據(jù)不一致。


小結(jié)一下,本地緩存的數(shù)據(jù)一致性解決方案,能徹底解決的是借助一致性hash的方案,但是成本比較高。所以,如非必要還是慎重決定要不要做本地緩存。


總結(jié)

好了,我們一起總結(jié)一下。

這次呢,Z哥先花了大量的篇幅和你討論「先寫(xiě)DB還是緩存」的問(wèn)題,并且?guī)銓訉由钊?,通過(guò)一點(diǎn)一點(diǎn)的演進(jìn)來(lái)闡述不同的解決方案。

然后與你討論了「本地緩存」的意義以及如何在「分布式緩存」和「數(shù)據(jù)庫(kù)」的基礎(chǔ)上做好數(shù)據(jù)一致性,這其中主要是多個(gè)本地緩存節(jié)點(diǎn)之間的數(shù)據(jù)同步問(wèn)題。

希望對(duì)你有所啟發(fā)。


這次的緩存實(shí)踐是一個(gè)非常好的例子,從中我們可以看到一件事情的精細(xì)化所帶來(lái)的復(fù)雜度需要更加的精細(xì)化去解決,但是又會(huì)帶來(lái)新的復(fù)雜度。所以作為技術(shù)人的你,需要無(wú)時(shí)無(wú)刻考慮該怎么權(quán)衡,而不是人云亦云



相關(guān)文章:

分布式系統(tǒng)關(guān)注點(diǎn)——360°全方位解讀「緩存」

分布式系統(tǒng)關(guān)注點(diǎn)——做了「負(fù)載均衡」就可以隨便加機(jī)器了嗎?


作者:Zachary

出處:https://www.cnblogs.com/Zacha...


如果你喜歡這篇文章,可以點(diǎn)一下文末的「」。

這樣可以給我一點(diǎn)反饋。: )

謝謝你的舉手之勞。


?關(guān)于作者:張帆(Zachary,個(gè)人微信號(hào):Zachary-ZF)。堅(jiān)持用心打磨每一篇高質(zhì)量原創(chuàng)。歡迎掃描下方的二維碼~。
定期發(fā)表原創(chuàng)內(nèi)容:架構(gòu)設(shè)計(jì)丨分布式系統(tǒng)丨產(chǎn)品丨運(yùn)營(yíng)丨一些思考。

如果你是初級(jí)程序員,想提升但不知道如何下手。又或者做程序員多年,陷入了一些瓶頸想拓寬一下視野。歡迎關(guān)注我的公眾號(hào)「跨界架構(gòu)師」,回復(fù)「技術(shù)」,送你一份我長(zhǎng)期收集和整理的思維導(dǎo)圖。

如果你是運(yùn)營(yíng),面對(duì)不斷變化的市場(chǎng)束手無(wú)策。又或者想了解主流的運(yùn)營(yíng)策略,以豐富自己的“倉(cāng)庫(kù)”。歡迎關(guān)注我的公眾號(hào)「跨界架構(gòu)師」,回復(fù)「運(yùn)營(yíng)」,送你一份我長(zhǎng)期收集和整理的思維導(dǎo)圖。

文章版權(quán)歸作者所有,未經(jīng)允許請(qǐng)勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉(zhuǎn)載請(qǐng)注明本文地址:http://systransis.cn/yun/62088.html

相關(guān)文章

  • 布式系統(tǒng)關(guān)注點(diǎn)(18)——「緩存穿透」和「緩存雪崩」到底啥區(qū)別?

    摘要:不過(guò),布隆過(guò)濾器有一個(gè)最大的缺點(diǎn),也是其為了高效利用內(nèi)存而付出的代價(jià),就是無(wú)法確保的準(zhǔn)確率。不過(guò)這種方式的優(yōu)勢(shì)是前面提到的,不會(huì)出現(xiàn)誤差,而布隆過(guò)濾器的錯(cuò)誤率會(huì)隨著位數(shù)的增加而減少,會(huì)不斷趨近于,但不會(huì)為。 ?如果第二次看到我的文章,歡迎文末掃碼訂閱我個(gè)人的公眾號(hào)(跨界架構(gòu)師)喲~ 本文長(zhǎng)度為2805字,建議閱讀8分鐘。堅(jiān)持原創(chuàng),每一篇都是用心之作~ 有句話說(shuō)得好,欲要使其毀滅,先要...

    tinyq 評(píng)論0 收藏0
  • 布式系統(tǒng)關(guān)注點(diǎn)(19)——深入淺出「異步」

    摘要:如果你對(duì)異步的了解比較模糊的話,這次可以帶你一次性深入淺出。同步異步任何事物都是有利有弊的。這也導(dǎo)致了在異步環(huán)境下做事務(wù)的成本更高。但是,異步在跨進(jìn)程通訊中更合適抽象成事件來(lái)進(jìn)行協(xié)作。 如果第二次看到我的文章,歡迎「文末」掃碼訂閱我個(gè)人的公眾號(hào)(跨界架構(gòu)師)喲~?每周五早8點(diǎn) 按時(shí)送達(dá)到公眾號(hào)。當(dāng)然了,也會(huì)時(shí)不時(shí)加個(gè)餐~ Z哥在前面的三篇文章里和你一起聊了「高性能」主題下與「緩存」...

    BicycleWarrior 評(píng)論0 收藏0

發(fā)表評(píng)論

0條評(píng)論

最新活動(dòng)
閱讀需要支付1元查看
<