快取資料庫更新策略
阿新 • • 發佈:2018-12-09
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來失效快取,基本能解決調快取失敗的情況。