面試官:3 種緩存更新策略是怎樣的?

作者:小林coding
八股文網(wǎng)站:xiaolincoding.com
大家好,我是小林。
今天跟大家聊聊,常見(jiàn)的 3 種緩存更新策略。
Cache Aside(旁路緩存)策略; Read/Write Through(讀穿 / 寫穿)策略; Write Back(寫回)策略;
實(shí)際開(kāi)發(fā)中,Redis 和 MySQL 的更新策略用的是 Cache Aside,另外兩種策略主要應(yīng)用在計(jì)算機(jī)系統(tǒng)里。
Cache Aside(旁路緩存)策略
Cache Aside(旁路緩存)策略是最常用的,應(yīng)用程序直接與「數(shù)據(jù)庫(kù)、緩存」交互,并負(fù)責(zé)對(duì)緩存的維護(hù),該策略又可以細(xì)分為「讀策略」和「寫策略」。

寫策略的步驟:
先更新數(shù)據(jù)庫(kù)中的數(shù)據(jù),再刪除緩存中的數(shù)據(jù)。
讀策略的步驟:
如果讀取的數(shù)據(jù)命中了緩存,則直接返回?cái)?shù)據(jù); 如果讀取的數(shù)據(jù)沒(méi)有命中緩存,則從數(shù)據(jù)庫(kù)中讀取數(shù)據(jù),然后將數(shù)據(jù)寫入到緩存,并且返回給用戶。
注意,寫策略的步驟的順序順序不能倒過(guò)來(lái),即不能先刪除緩存再更新數(shù)據(jù)庫(kù),原因是在「讀+寫」并發(fā)的時(shí)候,會(huì)出現(xiàn)緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致性的問(wèn)題。
舉個(gè)例子,假設(shè)某個(gè)用戶的年齡是 20,請(qǐng)求 A 要更新用戶年齡為 21,所以它會(huì)刪除緩存中的內(nèi)容。這時(shí),另一個(gè)請(qǐng)求 B 要讀取這個(gè)用戶的年齡,它查詢緩存發(fā)現(xiàn)未命中后,會(huì)從數(shù)據(jù)庫(kù)中讀取到年齡為 20,并且寫入到緩存中,然后請(qǐng)求 A 繼續(xù)更改數(shù)據(jù)庫(kù),將用戶的年齡更新為 21。

最終,該用戶年齡在緩存中是 20(舊值),在數(shù)據(jù)庫(kù)中是 21(新值),緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致。
為什么「先更新數(shù)據(jù)庫(kù)再刪除緩存」不會(huì)有數(shù)據(jù)不一致的問(wèn)題?
繼續(xù)用「讀 + 寫」請(qǐng)求的并發(fā)的場(chǎng)景來(lái)分析。
假如某個(gè)用戶數(shù)據(jù)在緩存中不存在,請(qǐng)求 A 讀取數(shù)據(jù)時(shí)從數(shù)據(jù)庫(kù)中查詢到年齡為 20,在未寫入緩存中時(shí)另一個(gè)請(qǐng)求 B 更新數(shù)據(jù)。它更新數(shù)據(jù)庫(kù)中的年齡為 21,并且清空緩存。這時(shí)請(qǐng)求 A 把從數(shù)據(jù)庫(kù)中讀到的年齡為 20 的數(shù)據(jù)寫入到緩存中。

最終,該用戶年齡在緩存中是 20(舊值),在數(shù)據(jù)庫(kù)中是 21(新值),緩存和數(shù)據(jù)庫(kù)數(shù)據(jù)不一致。從上面的理論上分析,先更新數(shù)據(jù)庫(kù),再刪除緩存也是會(huì)出現(xiàn)數(shù)據(jù)不一致性的問(wèn)題,但是在實(shí)際中,這個(gè)問(wèn)題出現(xiàn)的概率并不高。
因?yàn)榫彺娴膶懭胪ǔRh(yuǎn)遠(yuǎn)快于數(shù)據(jù)庫(kù)的寫入,所以在實(shí)際中很難出現(xiàn)請(qǐng)求 B 已經(jīng)更新了數(shù)據(jù)庫(kù)并且刪除了緩存,請(qǐng)求 A 才更新完緩存的情況。而一旦請(qǐng)求 A 早于請(qǐng)求 B 刪除緩存之前更新了緩存,那么接下來(lái)的請(qǐng)求就會(huì)因?yàn)榫彺娌幻卸鴱臄?shù)據(jù)庫(kù)中重新讀取數(shù)據(jù),所以不會(huì)出現(xiàn)這種不一致的情況。
Cache Aside 策略適合讀多寫少的場(chǎng)景,不適合寫多的場(chǎng)景,因?yàn)楫?dāng)寫入比較頻繁時(shí),緩存中的數(shù)據(jù)會(huì)被頻繁地清理,這樣會(huì)對(duì)緩存的命中率有一些影響。如果業(yè)務(wù)對(duì)緩存命中率有嚴(yán)格的要求,那么可以考慮兩種解決方案:
一種做法是在更新數(shù)據(jù)時(shí)也更新緩存,只是在更新緩存前先加一個(gè)分布式鎖,因?yàn)檫@樣在同一時(shí)間只允許一個(gè)線程更新緩存,就不會(huì)產(chǎn)生并發(fā)問(wèn)題了。當(dāng)然這么做對(duì)于寫入的性能會(huì)有一些影響; 另一種做法同樣也是在更新數(shù)據(jù)時(shí)更新緩存,只是給緩存加一個(gè)較短的過(guò)期時(shí)間,這樣即使出現(xiàn)緩存不一致的情況,緩存的數(shù)據(jù)也會(huì)很快過(guò)期,對(duì)業(yè)務(wù)的影響也是可以接受。
Read/Write Through(讀穿 / 寫穿)策略
Read/Write Through(讀穿 / 寫穿)策略原則是應(yīng)用程序只和緩存交互,不再和數(shù)據(jù)庫(kù)交互,而是由緩存和數(shù)據(jù)庫(kù)交互,相當(dāng)于更新數(shù)據(jù)庫(kù)的操作由緩存自己代理了。
Read Through 策略
先查詢緩存中數(shù)據(jù)是否存在,如果存在則直接返回,如果不存在,則由緩存組件負(fù)責(zé)從數(shù)據(jù)庫(kù)查詢數(shù)據(jù),并將結(jié)果寫入到緩存組件,最后緩存組件將數(shù)據(jù)返回給應(yīng)用。
Write Through 策略
當(dāng)有數(shù)據(jù)更新的時(shí)候,先查詢要寫入的數(shù)據(jù)在緩存中是否已經(jīng)存在:
如果緩存中數(shù)據(jù)已經(jīng)存在,則更新緩存中的數(shù)據(jù),并且由緩存組件同步更新到數(shù)據(jù)庫(kù)中,然后緩存組件告知應(yīng)用程序更新完成。 如果緩存中數(shù)據(jù)不存在,直接更新數(shù)據(jù)庫(kù),然后返回;
下面是 Read Through/Write Through 策略的示意圖:

Read Through/Write Through 策略的特點(diǎn)是由緩存節(jié)點(diǎn)而非應(yīng)用程序來(lái)和數(shù)據(jù)庫(kù)打交道,在我們開(kāi)發(fā)過(guò)程中相比 Cache Aside 策略要少見(jiàn)一些,原因是我們經(jīng)常使用的分布式緩存組件,無(wú)論是 Memcached 還是 Redis 都不提供寫入數(shù)據(jù)庫(kù)和自動(dòng)加載數(shù)據(jù)庫(kù)中的數(shù)據(jù)的功能。而我們?cè)谑褂帽镜鼐彺娴臅r(shí)候可以考慮使用這種策略。
Write Back(寫回)策略
Write Back(寫回)策略在更新數(shù)據(jù)的時(shí)候,只更新緩存,同時(shí)將緩存數(shù)據(jù)設(shè)置為臟的,然后立馬返回,并不會(huì)更新數(shù)據(jù)庫(kù)。對(duì)于數(shù)據(jù)庫(kù)的更新,會(huì)通過(guò)批量異步更新的方式進(jìn)行。
實(shí)際上,Write Back(寫回)策略也不能應(yīng)用到我們常用的數(shù)據(jù)庫(kù)和緩存的場(chǎng)景中,因?yàn)?Redis 并沒(méi)有異步更新數(shù)據(jù)庫(kù)的功能。
Write Back 是計(jì)算機(jī)體系結(jié)構(gòu)中的設(shè)計(jì),比如 CPU 的緩存、操作系統(tǒng)中文件系統(tǒng)的緩存都采用了 Write Back(寫回)策略。
Write Back 策略特別適合寫多的場(chǎng)景,因?yàn)榘l(fā)生寫操作的時(shí)候, 只需要更新緩存,就立馬返回了。比如,寫文件的時(shí)候,實(shí)際上是寫入到文件系統(tǒng)的緩存就返回了,并不會(huì)寫磁盤。
但是帶來(lái)的問(wèn)題是,數(shù)據(jù)不是強(qiáng)一致性的,而且會(huì)有數(shù)據(jù)丟失的風(fēng)險(xiǎn),因?yàn)榫彺嬉话闶褂脙?nèi)存,而內(nèi)存是非持久化的,所以一旦緩存機(jī)器掉電,就會(huì)造成原本緩存中的臟數(shù)據(jù)丟失。所以你會(huì)發(fā)現(xiàn)系統(tǒng)在掉電之后,之前寫入的文件會(huì)有部分丟失,就是因?yàn)?Page Cache 還沒(méi)有來(lái)得及刷盤造成的。
這里貼一張 CPU 緩存與內(nèi)存使用 Write Back 策略的流程圖:

有沒(méi)有覺(jué)得這個(gè)流程很熟悉?因?yàn)槲以趯?CPU 緩存文章的時(shí)候提到過(guò)。
推薦閱讀:
3 萬(wàn)字 + 40 張圖 | 突擊 40 道 Redis 常見(jiàn)面試題
趣說(shuō) | 數(shù)據(jù)庫(kù)和緩存如何保證一致性?
