前言
緩存是互聯(lián)網(wǎng)高并發(fā)系統(tǒng)里常用的組件笆载。由于多增加了一層,如果沒有正確的使用效果可能適得其反,諸如“緩存是刪除還是更新凉驻?”腻要,“先操作數(shù)據(jù)庫還是先操作緩存?”都是些老生常談的話題涝登,今天要介紹的是一個(gè)由 Facebook 提出的廣受認(rèn)可的緩存方案雄家。
緩存是刪除還是更新
- 緩存更新策略,如果緩存里面存的 value 是經(jīng)過序列化的對象胀滚,需要經(jīng)過反序列化設(shè)置新值等步驟更新成本高趟济,此時(shí)刪除緩存成本低 ,只需直接淘汰緩存咽笼,等待下次數(shù)據(jù)匯源在設(shè)置新的緩存顷编。
- 緩存更新策略,高并發(fā)場景有臟數(shù)據(jù)問題剑刑。
同時(shí)有請求A和請求B進(jìn)行更新操作媳纬,那么會(huì)出現(xiàn):
線程A更新了數(shù)據(jù)庫;
線程B更新了數(shù)據(jù)庫叛甫;
線程B更新了緩存层宫;
線程A更新了緩存;
這就出現(xiàn)請求A更新緩存應(yīng)該比請求B更新緩存早才對其监,但是因?yàn)榫W(wǎng)絡(luò)等原因萌腿,B卻比A更早更新了緩存。這就導(dǎo)致了臟數(shù)據(jù)抖苦,
總結(jié):更新緩存會(huì)帶來種種問題毁菱,直接刪除緩存比較簡單粗暴,穩(wěn)妥锌历。
先更新數(shù)據(jù)庫還是先操作緩存
- 先操作(刪除)緩存的情況贮庞,還是回到上面的高并發(fā)場景:
1. 請求A進(jìn)行寫操作,刪除緩存究西;
2. 請求B查詢發(fā)現(xiàn)緩存不存在窗慎;
3. 請求B去數(shù)據(jù)庫查詢得到舊值;
4. 請求B將舊值寫入緩存卤材;
5. 請求A將新值寫入數(shù)據(jù)庫遮斥;
此時(shí)如果緩存沒有設(shè)置超時(shí)時(shí)間,則緩存里面的數(shù)據(jù)會(huì)一直都是舊的數(shù)據(jù)扇丛。
- 先更新數(shù)據(jù)庫的情況术吗,這就是 Cache Aside Pattern 里的原則之一,下面分析下 case :
1. 緩存剛好失效帆精,請求A查詢數(shù)據(jù)庫较屿,得一個(gè)舊值隧魄;
2. 請求B將新值寫入數(shù)據(jù)庫;
3. 請求B刪除緩存隘蝎;
4. 請求A將查到的舊值寫入緩存购啄;
這時(shí)緩存里面確實(shí)是臟數(shù)據(jù)了,然而這種情況很小概率發(fā)生末贾。因?yàn)橹挥性诘?2 步寫數(shù)據(jù)庫的請求比第 1 步查詢數(shù)據(jù)的請求還快還會(huì)發(fā)生這種情況闸溃,由于數(shù)據(jù)庫的特性整吆,這種情況很少會(huì)存在拱撵,所以這種方案相對來說是比較可靠的。
Cache Aside Pattern
除了上面舉的例子表蝙,Cache Aside Pattern 還有幾個(gè)原則拴测。
失效:應(yīng)用程序先從cache取數(shù)據(jù),沒有得到府蛇,則從數(shù)據(jù)庫中取數(shù)據(jù)集索,成功后,放到緩存中汇跨;
命中:應(yīng)用程序從cache中取數(shù)據(jù)务荆,取到后返回;
更新:先把數(shù)據(jù)存到數(shù)據(jù)庫中穷遂,成功后函匕,再讓緩存失效;
前面兩點(diǎn)幾乎都是共識(shí)了也沒必要展開講了蚪黑,重點(diǎn)就是第 3 點(diǎn)盅惜。