緩存數據一致性 – 架構師峰會演講實錄
- 2021 年 5 月 17 日
- 筆記
Previously
緩存系統涉及的問題和知識點是比較多的,我主要分為以下幾個方面來跟大家探討:
- 穩定性
- 正確性
- 可觀測性
- 規範落地和工具建設
上篇 我們分析了緩存系統的穩定性,介紹了 go-zero 是怎麼解決緩存穿透、緩存擊穿、緩存雪崩問題的。比較淺顯易懂,且具有比較強的實戰意義,推薦一讀。
本文作為系列文章第二篇,主要跟大家探討『緩存數據一致性』
緩存正確性
上篇文章提到,我們引入緩存的初衷是為了減小DB壓力,增加系統穩定性,所以我們一開始關注的是緩存系統的穩定性。當穩定性解決之後,一般我們就會面臨數據正確性問題,可能會經常遇到『明明數據更新了,為啥還是顯示老的呢?』這類問題。這就是我們常說的『緩存數據一致性』問題了,接下來我們仔細下分析其產生的原因及應對方法。
數據更新常見做法
首先,我們講數據一致性的前提是我們DB的更新和緩存的刪除不會當成一個原子操作來看待,因為在高並發的場景下,我們不可能引入一個分佈式鎖來把這兩者綁定為一個原子操作,如果綁定的話就會很大程度上影響並發性能,而且增加系統複雜度,所以我們只會追求數據的最終一致性,且本文只針對非追求強一致性要求的高並發場景,金融支付等同學自行判斷。
常見數據更新方式有兩大類,其餘基本都是這兩類的變種:
- 先刪緩存,再更新數據庫
這種做法是遇到數據更新,我們先去刪除緩存,然後再去更新DB,如左圖。讓我們來看一下整個操作的流程:
- A請求需要更新數據,先刪除對應的緩存,還未更新DB
- B請求來讀取數據
- B請求看到緩存里沒有,就去讀取DB並將舊數據寫入緩存(臟數據)
- A請求更新DB
可以看到B請求將臟數據寫入了緩存,如果這是一個讀多寫少的數據,可能臟數據會存在比較長的時間(要麼有後續更新,要麼等待緩存過期),這是業務上不能接受的。
- 先更新數據庫,再刪除緩存
上圖的右側部分可以看到在A更新DB和刪除緩存之間B請求會讀取到老數據,因為此時A操作還沒有完成,並且這種讀到老數據的時間是非常短的,可以滿足數據最終一致性要求。
上圖可以看到我們用的是刪除緩存,而不是更新緩存,原因如下圖:
上圖我用操作代替了刪除或更新,當我們做刪除操作時,A先刪還是B先刪沒有關係,因為後續讀取請求都會從DB加載出最新數據;但是當我們對緩存做的是更新操作時,就會對A先更新緩存還是B先更新緩存敏感了,如果A後更新,那麼緩存里就又存在臟數據了,所以 go-zero 只使用刪除緩存的方式。
我們來一起看看完整的請求處理流程:
注意:不同顏色代表不同請求。
- 請求1更新DB
- 請求2查詢同一個數據,返回了老的數據,這個短時間內返回舊數據是可以接受的,滿足最終一致性
- 請求1刪除緩存
- 請求3再來請求時緩存里沒有,就會查詢數據庫,並回寫緩存再返回結果
- 後續的請求就會直接讀取緩存了
另外留一個問題大家可以思考下,對於下圖的場景,我們該怎麼應對?
如果你有好的解決方法或者想知道怎麼解決,歡迎 go-zero 社區微信群內交流,授人以魚不如授人以漁,求解的過程必將讓你收穫更多~~
未完待續
本文跟大家一起討論了緩存數據一致性問題,下一篇我來跟大家一起討論緩存系統的監控以及如何讓緩存代碼更規範、更少bug。
所有這些問題的解決方法都已包含在 go-zero 微服務框架里,如果你想要更好的了解 go-zero 項目,歡迎前往官方網站上學習具體的示例。
視頻回放地址
項目地址
歡迎使用 go-zero 並 star 支持我們!
微信交流群
關注『微服務實踐』公眾號並點擊 進群 獲取社區群二維碼。
go-zero 系列文章見『微服務實踐』公眾號