Cache Aside Pattern(旁路緩存模式)
對于讀請求
先讀cache朗鸠,再讀db
如果,cache hit却邓,則直接返回數(shù)據(jù)
如果毕荐,cache miss,則訪問db冕碟,并將數(shù)據(jù)set回緩存
對于寫請求
淘汰緩存拦惋,而不是更新緩存
先操作數(shù)據(jù)庫,再淘汰緩存
Cache Aside Pattern為什么建議淘汰緩存安寺,而不是更新緩存厕妖?
答:如果更新緩存,在并發(fā)寫時挑庶,可能出現(xiàn)數(shù)據(jù)不一致言秸。
如果采用set緩存。
在1和2兩個并發(fā)寫發(fā)生時迎捺,由于無法保證時序举畸,此時不管先操作緩存還是先操作數(shù)據(jù)庫,都可能出現(xiàn):
(1)請求1先操作數(shù)據(jù)庫凳枝,請求2后操作數(shù)據(jù)庫
(2)請求2先set了緩存抄沮,請求1后set了緩存
導致,數(shù)據(jù)庫與緩存之間的數(shù)據(jù)不一致岖瑰。
所以叛买,Cache Aside Pattern建議,delete緩存蹋订,而不是set緩存率挣。
Cache Aside Pattern為什么建議先操作數(shù)據(jù)庫,再操作緩存露戒?
答:如果先操作緩存椒功,在讀寫并發(fā)時,可能出現(xiàn)數(shù)據(jù)不一致智什。
架構師之路原文是:
如果先操作緩存动漾。
在1和2并發(fā)讀寫發(fā)生時,由于無法保證時序撩鹿,可能出現(xiàn):
(1)寫請求淘汰了緩存
(2)寫請求操作了數(shù)據(jù)庫(主從同步?jīng)]有完成)
(3)讀請求讀了緩存(cache miss)
(4)讀請求讀了從庫(讀了一個舊數(shù)據(jù))
(5)讀請求set回緩存(set了一個舊數(shù)據(jù))
(6)數(shù)據(jù)庫主從同步完成
導致谦炬,數(shù)據(jù)庫與緩存的數(shù)據(jù)不一致。
其實我認為就算沒有主從同步這個問題节沦,只有一臺數(shù)據(jù)庫键思,也會出現(xiàn)數(shù)據(jù)不一致的情況:
(1)寫請求淘汰了緩存
(2)此時進來一個讀請求,將舊的數(shù)據(jù)set進緩存
(3)寫請求操作了數(shù)據(jù)庫
這種情況的概率是很大的甫贯,因為大多數(shù)情況下的都是讀多寫少的場景吼鳞。
所以,Cache Aside Pattern建議叫搁,先操作數(shù)據(jù)庫赔桌,再操作緩存。
Cache Aside Pattern方案存在什么問題渴逻?
從并發(fā)角度:
比如疾党,一個是讀操作,但是沒有命中緩存(緩存剛好到期)惨奕,就會到數(shù)據(jù)庫中取數(shù)據(jù)雪位。而此時來了一個寫操作,寫完數(shù)據(jù)庫后梨撞,讓緩存失效雹洗,然后之前的那個讀操作再把老的數(shù)據(jù)放進去,所以會造成臟數(shù)據(jù)卧波。
這個案例理論上會出現(xiàn)时肿,但實際上出現(xiàn)的概率可能非常低,因為這個條件需要發(fā)生在讀緩存時緩 存失效港粱,而且有一個并發(fā)的寫操作螃成。實際上數(shù)據(jù)庫的寫操作會比讀操作慢得多,而且還要鎖表查坪, 而讀操作必需在寫操作前進入數(shù)據(jù)庫操作锈颗,又要晚于寫操作更新緩存,所有這些條件都具備的概率并不大咪惠。
所以不能完全避免并發(fā)導致的數(shù)據(jù)不一致問題击吱,只能從概率上保證。
分布式事務角度:
如果先操作數(shù)據(jù)庫遥昧,再淘汰緩存覆醇,在原子性被破壞時:
(1)修改數(shù)據(jù)庫成功了
(2)淘汰緩存失敗了
導致,數(shù)據(jù)庫與緩存的數(shù)據(jù)不一致炭臭。
但是
Cache Aside Pattern操作緩存失敗永脓,可以通過重試加打日志發(fā)現(xiàn)補救,也可以通過canal或者databus走kafka來失效緩存鞋仍,基本能解決調(diào)緩存失敗的情況常摧。