13 | 緩存的使用姿勢(shì)(一):如何選擇緩存的讀寫策略篮迎?

這次我們先講講緩存的讀寫策略。你可能覺(jué)得緩存的讀寫很簡(jiǎn)單示姿,只需要有限讀緩存甜橱,緩存不命中就從數(shù)據(jù)庫(kù)查詢,查詢到了就回種緩存栈戳。實(shí)際上針對(duì)不同的業(yè)務(wù)場(chǎng)景岂傲,緩存的讀寫策略也是不同的。

我們以標(biāo)準(zhǔn)的“緩存+數(shù)據(jù)庫(kù)”的場(chǎng)景為例子檀,剖析經(jīng)典的緩存讀寫策略以及它們適用的場(chǎng)景镊掖。這樣一來(lái),就可以在日常的工作中根據(jù)不同的場(chǎng)景選擇不同的讀寫策略褂痰。

Cache Aside(旁路緩存)策略

我們來(lái)考慮一種最簡(jiǎn)單的業(yè)務(wù)場(chǎng)景亩进,比如說(shuō)在你的電商系統(tǒng)中有一個(gè)用戶表,表中只有ID和年齡兩個(gè)字段缩歪,緩存中我們以ID為key存儲(chǔ)用戶的年齡信息归薛。那么當(dāng)我們要把ID為1的用戶的年齡從19變?yōu)?0改如何做?

你可能會(huì)產(chǎn)生這樣的思路:先更新數(shù)據(jù)中ID為1的記錄,再更新緩存中Key為1的數(shù)據(jù)苟翻。

這個(gè)思路會(huì)造成緩存和數(shù)據(jù)庫(kù)中的數(shù)據(jù)不一致韵卤。請(qǐng)看下圖 :

image.png

為什么會(huì)產(chǎn)生這個(gè)問(wèn)題呢?因?yàn)樽兏鼣?shù)據(jù)庫(kù)和變更緩存是兩個(gè)獨(dú)立的操作崇猫,而我們并沒(méi)有對(duì)操作做任何的并發(fā)控制沈条。那么當(dāng)兩個(gè)線程并發(fā)更新它們的時(shí)候,就會(huì)因?yàn)閷懭腠樞虻牟煌斐蓴?shù)據(jù)不一致诅炉。

另外蜡歹,直接更新緩存還會(huì)存在另一個(gè)問(wèn)題就是丟失更新。以我們的電商系統(tǒng)為例涕烧,假如電商系統(tǒng)的賬戶中有三個(gè)字段:ID月而、戶名和金額,這個(gè)時(shí)候緩存中存儲(chǔ)的就不只是金額信息议纯,而是完整的賬戶信息了父款。當(dāng)更新緩存中賬戶的金額時(shí),你需要從緩存中查詢完整的賬戶數(shù)據(jù)瞻凤,把金額變更后再寫入到緩存中憨攒。

這個(gè)過(guò)程也會(huì)有并發(fā)問(wèn)題,比如說(shuō)原有金額時(shí)20阀参,A請(qǐng)求從緩存督導(dǎo)數(shù)據(jù)肝集,并且把金額+1變更為21杏瞻,在未寫入請(qǐng)求之前又有請(qǐng)求B也讀到緩存數(shù)據(jù)后把金額+1,也變更為21赫模,兩個(gè)請(qǐng)求同時(shí)把金額寫回緩存,這時(shí)緩存里面的金額是21劣像,但是語(yǔ)氣金額數(shù)+2,這是個(gè)比較大的問(wèn)題闸婴。

要如何解決這個(gè)問(wèn)題呢对竣?其實(shí)我們可以在更新數(shù)據(jù)時(shí)不更新緩存,而是刪除緩存中的數(shù)據(jù)睛驳,在讀取數(shù)據(jù)時(shí),發(fā)現(xiàn)緩存中沒(méi)了數(shù)據(jù)后钥勋,再?gòu)臄?shù)據(jù)庫(kù)讀取數(shù)據(jù)驻啤,更新到緩存中。

image.png

這個(gè)策略就是我們使用緩存最常見(jiàn)的策略谤绳,Cache Aside策略(也叫旁路緩存策略),這個(gè)策略數(shù)據(jù)以數(shù)據(jù)庫(kù)中的數(shù)據(jù)為準(zhǔn)婿失,緩存中的數(shù)據(jù)是按需加載的。它可以分為讀策略和寫策略,其中讀策略的步驟是:

  • 從緩存中讀取數(shù)據(jù)稽穆;
  • 如果緩存命中,則直接返回?cái)?shù)據(jù)卖擅;
  • 如果緩存不命中,則從數(shù)據(jù)庫(kù)中查詢數(shù)據(jù)健提;
  • 查詢到數(shù)據(jù)后琳猫,將數(shù)據(jù)寫入到緩存中,并且返回給用戶私痹。

寫策略的步驟是:

  • 更新數(shù)據(jù)庫(kù)中的記錄脐嫂;
  • 刪除緩存記錄统刮。

在寫策略中,能否先刪除緩存账千,后更新數(shù)據(jù)呢侥蒙?答案是不行的,因?yàn)檫@樣也有可能會(huì)出現(xiàn)緩存數(shù)據(jù)不一致的問(wèn)題匀奏,我們以用戶表的場(chǎng)景為例解釋一下鞭衩。

image.png

那么像Cache Aside策略這樣先更新數(shù)據(jù)庫(kù),后刪除緩存就沒(méi)問(wèn)題了么娃善?其實(shí)在理論上還是有缺陷的论衍。

image.png

不過(guò)這種問(wèn)題出現(xiàn)的概率并不高,因?yàn)?strong>緩存的寫入通常遠(yuǎn)遠(yuǎn)快于數(shù)據(jù)庫(kù)的寫入聚磺,所以在實(shí)際中很難出現(xiàn)請(qǐng)求B已經(jīng)更新了數(shù)據(jù)庫(kù)并且清空了緩存坯台,請(qǐng)求A才更新完緩存的情況。而一旦請(qǐng)求A早于B清空緩存之前更新了緩存瘫寝,那么接下來(lái)的請(qǐng)求就會(huì)因?yàn)榫彺鏋榭昭牙伲鴱臄?shù)據(jù)庫(kù)中重新加載數(shù)據(jù),所以不會(huì)出現(xiàn)這不一致的情況焕阿。

****Cache Aside策略是我們?nèi)粘i_(kāi)發(fā)中經(jīng)常使用的緩存策略略咪啡,不過(guò)我們?cè)谑褂脮r(shí)也要學(xué)會(huì)依情況而變。**比如說(shuō)當(dāng)新注冊(cè)一個(gè)用戶暮屡,按照這個(gè)更新策略撤摸,你要寫數(shù)據(jù)庫(kù),然后清理緩存(當(dāng)然緩存中沒(méi)有數(shù)據(jù)給你清理)栽惶〕盍铮可當(dāng)我注冊(cè)用戶后立即讀取用戶信息,并且數(shù)據(jù)庫(kù)主從分離時(shí)外厂,會(huì)出現(xiàn)因?yàn)橹鲝难舆t所以讀不到用戶信息的情況。

而解決這個(gè)問(wèn)題的方法是在插入新數(shù)據(jù)到DB之后寫入緩存代承,這樣后續(xù)的請(qǐng)求就會(huì)從緩存中讀到數(shù)據(jù)了汁蝶。并且因?yàn)槭切伦?cè)的用戶,所以不會(huì)出現(xiàn)并發(fā)更新用戶信息的情況论悴。

Cache Aside存在的最大的問(wèn)題是當(dāng)寫入比較頻繁時(shí)掖棉,緩存你中的數(shù)據(jù)會(huì)被頻繁的清理,這樣會(huì)對(duì)緩存的命中率有一些影響膀估。如果你的業(yè)務(wù)對(duì)緩存命中率有嚴(yán)格要求幔亥,那么可以考慮兩種解決方案:
1.在更新數(shù)據(jù)時(shí)也更新緩存,只是在更新緩存前先加一個(gè)分布式鎖察纯,因?yàn)檫@樣在同一時(shí)間只允許一個(gè)線程更新緩存帕棉,就不會(huì)產(chǎn)生并發(fā)問(wèn)題了针肥。當(dāng)然這么做對(duì)于寫入的性能會(huì)有一些影響;
2.另一種做法同樣也是在更新數(shù)據(jù)時(shí)更新緩存香伴,只是給緩存加一個(gè)較短的過(guò)期時(shí)間慰枕,這樣即使出現(xiàn)緩存不一致的情況,緩存的數(shù)據(jù)也會(huì)很快的過(guò)期即纲,對(duì)業(yè)務(wù)的影響也是可以承受的具帮。

當(dāng)然,除了這個(gè)策略低斋,在計(jì)算機(jī)領(lǐng)域還有其他幾種經(jīng)典的緩存策略蜂厅,它們也有各自適用的使用場(chǎng)景。

Read/Write Through(讀穿/寫穿)策略

這個(gè)策略的核心原則是用戶只與緩存打交道膊畴,由緩存和DB通信掘猿,寫入或者讀取數(shù)據(jù)。就比如你不能越級(jí)匯報(bào)巴比。

Write Through的策略是這樣的:先查詢要寫入的數(shù)據(jù)在緩存中是否已經(jīng)存在术奖,如果存在,則更新緩存中的數(shù)據(jù)轻绞,并且由緩存組件同步更新到DB中采记。如果緩存中數(shù)據(jù)不存在,我們把這種情況叫做“Write Miss(寫失效)”政勃。

一般來(lái)說(shuō)唧龄,我們可以選擇兩種“Write Miss”方式:一個(gè)是“Write Allocate(按寫分配)”,做法是寫入緩存相應(yīng)位置奸远,再由緩存組件同步更新到DB中既棺;另一個(gè)是“No-write allocate(不按寫分配)”,做法是不寫入緩存中懒叛,而是直接更新到DB中丸冕。
在Write Through策略中,我們一般選擇“不按寫分配”方式薛窥,原因是無(wú)論采用哪種“Write Miss”方式胖烛,我們都需要同步將數(shù)據(jù)更新到數(shù)據(jù)庫(kù)中,而“不按寫分配”方式相比“按寫分配”還減少了一次緩存的寫入诅迷,能夠提升寫入的性能佩番。

Read Though策略就簡(jiǎn)單一些,它的步驟是這樣的:先查詢緩存中數(shù)據(jù)是否存在罢杉,如果存在則直接返回趟畏,如果不存在,則由緩存組件負(fù)責(zé)從數(shù)據(jù)庫(kù)中同步加載數(shù)據(jù)滩租。
下面是Read Though/ Write Though策略的示意圖:


image.png

Read Though/ Write Though策略的特點(diǎn)是由緩存節(jié)點(diǎn)而非用戶來(lái)和數(shù)據(jù)庫(kù)打交道赋秀,在我們開(kāi)發(fā)過(guò)程中相比Cache Aside策略要少見(jiàn)一些利朵,原因是我們經(jīng)常使用分布式緩存組件,無(wú)論是Memcached還是redis都不提供寫入DB沃琅,或者自動(dòng)加載DB中數(shù)據(jù)的功能哗咆。而我們?cè)谑褂帽镜鼐彺娴臅r(shí)候可以考慮使用這種策略,比如在上一節(jié)中提到的本地緩存Guava Cached中的Loading就有Read Though策略的影子益眉。

我們看到Write Though策略中寫數(shù)據(jù)庫(kù)是同步的晌柬,這對(duì)于性能來(lái)說(shuō)會(huì)有比較大的影響,因?yàn)橄鄬?duì)于寫緩存郭脂,同步寫數(shù)據(jù)庫(kù)的延遲就要高很多了name我們可否異步地更新數(shù)據(jù)庫(kù)年碘?

Write Back(寫回)策略

這個(gè)策略的核心思想是在寫入數(shù)據(jù)時(shí)只寫入緩存,并且把緩存塊兒標(biāo)記為“臟”的展鸡。而臟塊只有被再次使用時(shí)才會(huì)將其中的數(shù)據(jù)寫入到后端存儲(chǔ)中屿衅。
需要注意的是,在“write Miss”的情況下莹弊,我們采用的是“按寫分配”的方式涤久,也就是在寫入后端存儲(chǔ)的同時(shí)要寫入緩存,這樣我們?cè)谥蟮膶懻?qǐng)求中都只需要更新緩存即可,而無(wú)需更新后端存儲(chǔ)了。

image.png

如果使用Write Back策略的話单芜,讀的策略也有一些變化了。我們?cè)谧x取緩存時(shí)如果發(fā)現(xiàn)緩存命中則直接返回緩存數(shù)據(jù)蔗彤。如果不命中則尋找一個(gè)可用的緩存塊兒,如果這個(gè)緩存塊是“臟”的疯兼,就把緩存塊兒中之前的數(shù)據(jù)寫入到后端存儲(chǔ)中然遏,并且從后端存儲(chǔ)加載數(shù)據(jù)到緩存塊,如果不是臟的吧彪,則由緩存組件將后端存儲(chǔ)中的數(shù)據(jù)加載到緩存中待侵,最后我們將緩存設(shè)置為不是臟的,返回?cái)?shù)據(jù)就好了姨裸。

image.png

其實(shí)這種策略不能被應(yīng)用到我們常用的DB和緩存場(chǎng)景中诫给,它是計(jì)算機(jī)體系結(jié)構(gòu)中的設(shè)計(jì),比如我們?cè)谙虼疟P中寫數(shù)據(jù)時(shí)采用的就是這種策略啦扬。
無(wú)論是操作系統(tǒng)層面的Page Cache,還是日志的異步刷盤凫碌,亦或是消息隊(duì)列中消息的異步寫入磁盤扑毡,大多采用了這種策略。因?yàn)檫@個(gè)策略在性能上的優(yōu)勢(shì)毋庸置疑盛险,它避免了直接寫磁盤造成層的隨機(jī)寫問(wèn)題瞄摊,畢竟寫內(nèi)存和寫磁盤的隨機(jī)I/O的延遲相差了幾個(gè)數(shù)量級(jí)勋又。

但因?yàn)榫彺嬉话闶褂脙?nèi)存,而內(nèi)存是非持久化的换帜,所以一旦緩存機(jī)器掉電楔壤,就會(huì)造成原本緩存中的臟塊數(shù)據(jù)丟失。所以你會(huì)發(fā)現(xiàn)系統(tǒng)在掉電之后惯驼,之前寫入的文件會(huì)有部分丟失蹲嚣,就是因?yàn)镻age Cache還沒(méi)有來(lái)得及刷盤造成的。

當(dāng)然你依然可以在一些場(chǎng)景下使用這個(gè)策略祟牲,在使用時(shí)隙畜,建議是:在向低速設(shè)備寫入數(shù)據(jù)的時(shí)候,可以再內(nèi)存里先暫存一段時(shí)間的數(shù)據(jù)说贝,甚至做一些統(tǒng)計(jì)匯總议惰,然后定時(shí)的刷新到低速設(shè)備上。比如說(shuō)你在統(tǒng)計(jì)接口響應(yīng)時(shí)間的時(shí)候乡恕,需要將每次請(qǐng)求的響應(yīng)時(shí)間打印到日志中言询,然后監(jiān)控系統(tǒng)手機(jī)日之后再做統(tǒng)計(jì)。但是如果每次請(qǐng)求都打印日志無(wú)疑會(huì)增加磁盤的I/O傲宜,那么不如把一段時(shí)間的響應(yīng)時(shí)間暫存起來(lái)运杭,經(jīng)過(guò)簡(jiǎn)單的統(tǒng)計(jì)平均耗時(shí),每個(gè)耗時(shí)區(qū)間的請(qǐng)求數(shù)量等蛋哭,然后定時(shí)的批量打印到日志中县习。

內(nèi)容總結(jié)

1.Cache Aside是我們?cè)谑褂梅植际骄彺鏁r(shí)最常用的策略,可以在實(shí)際工作中直接拿來(lái)使用谆趾。
2.Read/Write Through和Write Back策略需要緩存組件的支持躁愿,所以比較適合在實(shí)現(xiàn)本地緩存組件的時(shí)候使用;
3.Write Back策略是計(jì)算機(jī)體系結(jié)構(gòu)中的策略沪蓬,不過(guò)寫入策略中的只寫緩存彤钟,異步寫入后端存儲(chǔ)的策略倒是有很多的應(yīng)用場(chǎng)景。

而且跷叉,你還需要了解逸雹,我們今天提到的策略都是標(biāo)準(zhǔn)的使用姿勢(shì),在實(shí)際開(kāi)發(fā)過(guò)程中需要結(jié)合實(shí)際的業(yè)務(wù)特點(diǎn)靈活使用甚至加以改造云挟。這些業(yè)務(wù)特點(diǎn)包括但不僅限于:整體的數(shù)量級(jí)情況梆砸,訪問(wèn)的讀寫比例的情況,對(duì)于數(shù)據(jù)的不一致時(shí)間的容忍度园欣,對(duì)于緩存命中率的要求等等帖世。理論結(jié)合實(shí)踐,具體肩況具體分析沸枯,你才能得到更好的解決方案

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末日矫,一起剝皮案震驚了整個(gè)濱河市赂弓,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌哪轿,老刑警劉巖盈魁,帶你破解...
    沈念sama閱讀 216,470評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異窃诉,居然都是意外死亡杨耙,警方通過(guò)查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,393評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門褐奴,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)按脚,“玉大人,你說(shuō)我怎么就攤上這事敦冬「ò幔” “怎么了?”我有些...
    開(kāi)封第一講書人閱讀 162,577評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵脖旱,是天一觀的道長(zhǎng)堪遂。 經(jīng)常有香客問(wèn)我,道長(zhǎng)萌庆,這世上最難降的妖魔是什么溶褪? 我笑而不...
    開(kāi)封第一講書人閱讀 58,176評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮践险,結(jié)果婚禮上猿妈,老公的妹妹穿的比我還像新娘。我一直安慰自己巍虫,他們只是感情好彭则,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,189評(píng)論 6 388
  • 文/花漫 我一把揭開(kāi)白布。 她就那樣靜靜地躺著占遥,像睡著了一般俯抖。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上瓦胎,一...
    開(kāi)封第一講書人閱讀 51,155評(píng)論 1 299
  • 那天芬萍,我揣著相機(jī)與錄音,去河邊找鬼搔啊。 笑死柬祠,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的负芋。 我是一名探鬼主播瓶盛,決...
    沈念sama閱讀 40,041評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了惩猫?” 一聲冷哼從身側(cè)響起,我...
    開(kāi)封第一講書人閱讀 38,903評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤蚜点,失蹤者是張志新(化名)和其女友劉穎轧房,沒(méi)想到半個(gè)月后,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體绍绘,經(jīng)...
    沈念sama閱讀 45,319評(píng)論 1 310
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡奶镶,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,539評(píng)論 2 332
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了陪拘。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片厂镇。...
    茶點(diǎn)故事閱讀 39,703評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖左刽,靈堂內(nèi)的尸體忽然破棺而出捺信,到底是詐尸還是另有隱情,我是刑警寧澤欠痴,帶...
    沈念sama閱讀 35,417評(píng)論 5 343
  • 正文 年R本政府宣布迄靠,位于F島的核電站,受9級(jí)特大地震影響喇辽,放射性物質(zhì)發(fā)生泄漏掌挚。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,013評(píng)論 3 325
  • 文/蒙蒙 一菩咨、第九天 我趴在偏房一處隱蔽的房頂上張望吠式。 院中可真熱鬧,春花似錦抽米、人聲如沸特占。這莊子的主人今日做“春日...
    開(kāi)封第一講書人閱讀 31,664評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)摩钙。三九已至,卻和暖如春查辩,著一層夾襖步出監(jiān)牢的瞬間胖笛,已是汗流浹背。 一陣腳步聲響...
    開(kāi)封第一講書人閱讀 32,818評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工宜岛, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留长踊,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,711評(píng)論 2 368
  • 正文 我出身青樓萍倡,卻偏偏與公主長(zhǎng)得像身弊,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,601評(píng)論 2 353

推薦閱讀更多精彩內(nèi)容

  • 一阱佛、引言 本文談及的是后臺(tái)業(yè)務(wù)服務(wù)緩存問(wèn)題帖汞,在構(gòu)建和優(yōu)化業(yè)務(wù)服務(wù)時(shí),第一想到的應(yīng)該是優(yōu)化數(shù)據(jù)庫(kù)凑术,比如數(shù)據(jù)庫(kù)模型設(shè)計(jì)...
    東_山_郎閱讀 1,142評(píng)論 0 8
  • 理論總結(jié) 它要解決什么樣的問(wèn)題翩蘸? 數(shù)據(jù)的訪問(wèn)、存取淮逊、計(jì)算太慢催首、太不穩(wěn)定、太消耗資源泄鹏,同時(shí)郎任,這樣的操作存在重復(fù)性。因...
    jiangmo閱讀 2,849評(píng)論 0 11
  • PS:轉(zhuǎn)載自《架構(gòu)師之路》歼疮,覺(jué)得受益匪淺,故收錄之 緩存誤用 緩存诈唬,是互聯(lián)網(wǎng)分層架構(gòu)中韩脏,非常重要的一個(gè)部分,通常用...
    Huang遠(yuǎn)閱讀 11,231評(píng)論 4 27
  • 緩存誤用 緩存吹散,是互聯(lián)網(wǎng)分層架構(gòu)中,非常重要的一個(gè)部分八酒,通常用它來(lái)降低數(shù)據(jù)庫(kù)壓力空民,提升系統(tǒng)整體性能,縮短訪問(wèn)時(shí)間羞迷。...
    叫我峰兄閱讀 1,214評(píng)論 0 0
  • 今天是加入運(yùn)營(yíng)學(xué)院的第五天界轩,走完一天日程,對(duì)幾個(gè)點(diǎn)可算是有收獲衔瓮,不算辜負(fù)今日時(shí)光浊猾。 1.認(rèn)定目標(biāo)。 今晚聽(tīng)了娜兒大...
    牛魔王愛(ài)寫作閱讀 207評(píng)論 0 1