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 專案,歡迎前往官方網站上學習具體的示例,
視頻回放地址
ArchSummit架構師峰會-海量并發下的快取架構設計
專案地址
https://github.com/tal-tech/go-zero
歡迎使用 go-zero 并 star 支持我們!
微信交流群
關注『微服務實踐』公眾號并點擊 進群 獲取社區群二維碼,
go-zero 系列文章見『微服務實踐』公眾號
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/285528.html
標籤:Go
下一篇:行程內快取助你提高并發能力!
