快取一致性解決方案

2021-10-11 07:26:19 字數 2017 閱讀 6023

通常為了提公升使用者體驗,專案裡面一些熱點資料我們會把它放入快取裡面,以商品庫存為例我們就可以把商品對應的庫存資料放入redis,查詢資料時先去redis裡面找,沒有找到再訪問資料庫,如果庫存資料有更新就同時去更新redis的快取資料,以此達到減輕資料庫壓力。

這種對快取資料的處理看似沒有問題,實際上在高併發下就會出現快取和資料庫資料不一致的情況,

方式一:先更新資料庫,再更新快取

比如,高併發下有兩個執行緒a,b,redis快取庫存和資料庫庫存資料都是100

(1)執行緒a更新了資料庫,庫存-1,99

(2)執行緒b更新了資料庫,庫存-1,98

(3)執行緒b更新了快取,快取庫存現在是98

(4)執行緒a更新了快取,快取庫存現在是99

如果(3)先更新了快取,最後快取庫存就變成了99,實際資料庫庫存資料為98,出現了髒資料。而且這種方式對於需要經過一系列複雜的計算再寫入快取的情況,無疑是浪費效能的。每次寫入資料庫後,都再次計算寫入快取的值,顯然,刪除快取更為適合。

方式二:先刪除快取,再更新資料庫

高併發下有兩個請求a,b

(1)請求a進行寫操作,刪除快取

(2)請求b查詢發現快取不存在

(3)請求b去資料庫查詢得到舊值

(4)請求b將舊值寫入快取

(5)請求a將新值寫入資料庫

按照上面的執行過程就會出現髒資料,快取中存的永遠是舊值。

解決這個問題可以用採用延時雙刪策略

對於寫操作:

(1)先刪掉快取

(2)再更新資料庫(這兩步和原來一樣)

(3)休眠1秒,再次刪除快取

針對上面的情形,讀者應該自行評估自己的專案的讀資料業務邏輯的耗時。然後寫資料的休眠時間則在讀資料業務邏輯的耗時基礎上,加幾百ms即可。這麼做的目的,就是確保讀請求結束,寫請求可以刪除讀請求造成的快取髒資料。

繼續深入,

如果專案用了mysql的讀寫分離架構怎麼辦?

造成資料不一致的原因如下,還是兩個請求,乙個請求a進行更新操作,另乙個請求b進行查詢操作

(1)請求a進行寫操作,刪除快取

(2)請求a將資料寫入資料庫了,

(3)請求b查詢快取發現,快取沒有值

(4)請求b去從庫查詢,這時,還沒有完成主從同步,因此查詢到的是舊值

(5)請求b將舊值寫入快取

(6)資料庫完成主從同步,從庫變為新值

這樣就出現了髒資料,所以這時候使用雙刪延時策略。只是,睡眠時間修改為在主從同步的延時時間基礎上,加幾百ms。

那麼對於延遲雙刪策略真的就能完全保證資料一致性嗎?

其實不然,如果第二次刪除失敗,就會出現如下情形。還是有兩個請求,乙個請求a進行更新操作,另乙個請求b進行查詢操作,為了方便,假設是單庫:

(1)請求a進行寫操作,刪除快取

(2)請求b查詢發現快取不存在

(3)請求b去資料庫查詢得到舊值

(4)請求b將舊值寫入快取

(5)請求a將新值寫入資料庫

(6)請求a試圖去刪除請求b寫入對快取值,結果失敗了。

對於這種情況,可以提供乙個保障的重試機制

流程如下所示

(1)更新資料庫資料;

(2)快取因為種種問題刪除失敗

(3)將需要刪除的key傳送至訊息佇列

(4)自己消費訊息,獲得需要刪除的key

(5)繼續重試刪除操作,直到成功

解決快取一致性還有一種方案提出維護乙個記憶體佇列的方式,實現起來比較複雜,也可以參考下

傳送門:

快取一致性解決

根據上一章的學習,我們現在就可以用redisson框架來解決分布式鎖的問題,進行修改獲取 二級分類 如下 todo 產生堆外記憶體溢位異常 outofdirectmemoryeror override public map getcatalogjson map result json.parseob...

緩衝架構一致性解決方案

對響應要求高的系統,架構時都會在增加緩衝伺服器 redis memcachd 那必須面對的問題就是如何保證緩衝和資料庫之間資料一致性問題。方案1 查詢直接查詢緩衝,更新操作同時更新緩衝資料 存在問題就是必須兩個更新同時成功,一方不成功則導致資料不一致 方案2 mysql binlog增量訂閱消費 訊...

session一致性的解決方案

伺服器為每個使用者建立乙個會話,儲存使用者的相關資訊,以便多次請求能夠定位到同乙個上下文,這個相關資訊就是session。這樣,當使用者在應用程式的web頁之間跳轉時,儲存在session物件中的變數將不會丟失,而是在整個使用者會話中一直存在下去。session是對http無狀態協議的補充,達到狀態...