在分布式系統(tǒng)中,通過使用數(shù)據(jù)庫+緩存已經(jīng)成為提升系統(tǒng)性能、降低訪問延遲的常用方法。但隨著業(yè)務(wù)復(fù)雜性的增加,該如何在數(shù)據(jù)庫與緩存之間實現(xiàn)雙寫一致性,確保數(shù)據(jù)的準確性和一致性呢?具體方案有以下幾種。
先寫緩存,在寫數(shù)據(jù)庫
如果先寫緩存成功,寫數(shù)據(jù)庫異常,那么緩存會出現(xiàn)臟數(shù)據(jù)的情況。
?
先寫數(shù)據(jù)庫,在寫緩存
如果寫數(shù)據(jù)庫成功,寫緩存失敗,那么緩存保留舊數(shù)據(jù),也會出現(xiàn)臟數(shù)據(jù)情況。
理論可以進行數(shù)據(jù)庫事務(wù)回滾操作,但是在高并發(fā)情況不要把他們放到一個事務(wù)里面去,容易造成死鎖問題。
而且高并發(fā)下,由于A線程寫緩存有延遲,后面的B線程先寫了緩存,最后A線程再寫緩存,這時也出現(xiàn)了臟數(shù)據(jù)。
先刪緩存,再寫數(shù)據(jù)庫
這個在高并發(fā)下,如果A線程刪除緩存后,寫數(shù)據(jù)庫時有延遲,那么B線程發(fā)生讀操作會把數(shù)據(jù)庫的內(nèi)容更新到緩存中,也會導(dǎo)致數(shù)據(jù)不一致的情況。
針對上面的問題可以使用緩存雙刪的方案。即寫完數(shù)據(jù)庫后,隔一段時間如500ms再刪一次緩存
?
先寫數(shù)據(jù)庫,再刪緩存(常用方案)
當出現(xiàn)以下場景時也會出現(xiàn)數(shù)據(jù)不一致問題:
A線程讀操作:緩存失效時,從數(shù)據(jù)庫讀取數(shù)據(jù),在寫緩存的過程中,下面的B線程先執(zhí)行完操作。
B線程寫操作:更新數(shù)據(jù)庫后,刪除緩存。
但是這場景出現(xiàn)概率比較少,因為需要同時滿足:
- 緩存剛好失效
- A線程的寫緩存延遲要比B線程寫數(shù)據(jù)庫+刪除緩存的時間更長(但一般是寫數(shù)據(jù)庫的時間會更耗時)
優(yōu)化:刪緩存失敗需要要重試機制(可以異步重試3次--考慮分布式定時任務(wù) elastic-job、rabbitmq 等,不要影響性能), 如果還是失敗,寫入數(shù)據(jù)庫,后續(xù)再處理