Cache Aside Pattern(旁路緩存模式)
對於讀請求
先讀cache,再讀db
如果,cache hit,則直接返回數據
如果,cache miss,則訪問db,並將數據set回緩存
對於寫請求
淘汰緩存,而不是更新緩存
先操作數據庫,再淘汰緩存
Cache Aside Pattern為什么建議淘汰緩存,而不是更新緩存?
答:如果更新緩存,在並發寫時,可能出現數據不一致。
如果采用set緩存。
在1和2兩個並發寫發生時,由於無法保證時序,此時不管先操作緩存還是先操作數據庫,都可能出現:
(1)請求1先操作數據庫,請求2后操作數據庫
(2)請求2先set了緩存,請求1后set了緩存
導致,數據庫與緩存之間的數據不一致。
所以,Cache Aside Pattern建議,delete緩存,而不是set緩存。
Cache Aside Pattern為什么建議先操作數據庫,再操作緩存?
答:如果先操作緩存,在讀寫並發時,可能出現數據不一致。
架構師之路原文是:
如果先操作緩存。
在1和2並發讀寫發生時,由於無法保證時序,可能出現:
(1)寫請求淘汰了緩存
(2)寫請求操作了數據庫(主從同步沒有完成)
(3)讀請求讀了緩存(cache miss)
(4)讀請求讀了從庫(讀了一個舊數據)
(5)讀請求set回緩存(set了一個舊數據)
(6)數據庫主從同步完成
導致,數據庫與緩存的數據不一致。
其實我認為就算沒有主從同步這個問題,只有一台數據庫,也會出現數據不一致的情況:
(1)寫請求淘汰了緩存
(2)此時進來一個讀請求,將舊的數據set進緩存
(3)寫請求操作了數據庫
這種情況的概率是很大的,因為大多數情況下的都是讀多寫少的場景。
所以,Cache Aside Pattern建議,先操作數據庫,再操作緩存。
Cache Aside Pattern方案存在什么問題?
從並發角度 :
比如,一個是讀操作,但是沒有命中緩存(緩存剛好到期),就會到數據庫中取數據。而此時來了一個寫操作,寫完數據庫后,讓緩存失效,然后之前的那個讀操作再把老的數據放進去,所以會造成臟數據。
這個案例理論上會出現,但實際上出現的概率可能非常低,因為這個條件需要發生在讀緩存時緩 存失效,而且有一個並發的寫操作。實際上數據庫的寫操作會比讀操作慢得多,而且還要鎖表, 而讀操作必需在寫操作前進入數據庫操作,又要晚於寫操作更新緩存,所有這些條件都具備的概率並不大。
所以不能完全避免並發導致的數據不一致問題,只能從概率上保證。
分布式事務角度 :
如果先操作數據庫,再淘汰緩存,在原子性被破壞時:
(1)修改數據庫成功了
(2)淘汰緩存失敗了
導致,數據庫與緩存的數據不一致。
但是
Cache Aside Pattern操作緩存失敗,可以通過重試加打日志發現補救,也可以通過canal或者databus走kafka來失效緩存,基本能解決調緩存失敗的情況。