通常為了提公升使用者體驗,專案裡面一些熱點資料我們會把它放入快取裡面,以商品庫存為例我們就可以把商品對應的庫存資料放入redis,查詢資料時先去redis裡面找,沒有找到再訪問資料庫,如果庫存資料有更新就同時去更新redis的快取資料,以此達到減輕資料庫壓力。
這種對快取資料的處理看似沒有問題,實際上在高併發下就會出現快取和資料庫資料不一致的情況,
方式一:先更新資料庫,再更新快取
比如,高併發下有兩個執行緒a,b,redis快取庫存和資料庫庫存資料都是100
(1)執行緒a更新了資料庫,庫存-1,99如果(3)先更新了快取,最後快取庫存就變成了99,實際資料庫庫存資料為98,出現了髒資料。而且這種方式對於需要經過一系列複雜的計算再寫入快取的情況,無疑是浪費效能的。每次寫入資料庫後,都再次計算寫入快取的值,顯然,刪除快取更為適合。(2)執行緒b更新了資料庫,庫存-1,98
(3)執行緒b更新了快取,快取庫存現在是98
(4)執行緒a更新了快取,快取庫存現在是99
方式二:先刪除快取,再更新資料庫
高併發下有兩個請求a,b
(1)請求a進行寫操作,刪除快取按照上面的執行過程就會出現髒資料,快取中存的永遠是舊值。(2)請求b查詢發現快取不存在
(3)請求b去資料庫查詢得到舊值
(4)請求b將舊值寫入快取
(5)請求a將新值寫入資料庫
解決這個問題可以用採用延時雙刪策略:
對於寫操作:
(1)先刪掉快取針對上面的情形,讀者應該自行評估自己的專案的讀資料業務邏輯的耗時。然後寫資料的休眠時間則在讀資料業務邏輯的耗時基礎上,加幾百ms即可。這麼做的目的,就是確保讀請求結束,寫請求可以刪除讀請求造成的快取髒資料。(2)再更新資料庫(這兩步和原來一樣)
(3)休眠1秒,再次刪除快取
繼續深入,
如果專案用了mysql的讀寫分離架構怎麼辦?
造成資料不一致的原因如下,還是兩個請求,乙個請求a進行更新操作,另乙個請求b進行查詢操作
(1)請求a進行寫操作,刪除快取這樣就出現了髒資料,所以這時候使用雙刪延時策略。只是,睡眠時間修改為在主從同步的延時時間基礎上,加幾百ms。(2)請求a將資料寫入資料庫了,
(3)請求b查詢快取發現,快取沒有值
(4)請求b去從庫查詢,這時,還沒有完成主從同步,因此查詢到的是舊值
(5)請求b將舊值寫入快取
(6)資料庫完成主從同步,從庫變為新值
那麼對於延遲雙刪策略真的就能完全保證資料一致性嗎?
其實不然,如果第二次刪除失敗,就會出現如下情形。還是有兩個請求,乙個請求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無狀態協議的補充,達到狀態...