為什么說延時雙刪很扯淡


redis和mysql數據一致性的問題

在這里,我們討論三種更新策略:

  1. 先更新緩存,再更新數據庫
  2. 先更新數據庫,再更新緩存
  3. 先刪除緩存,再更新數據庫
  4. 先更新數據庫,再刪除緩存

第一種,先更新緩存,再更新數據庫

問題:更新緩存成功,更新數據庫失敗,導致數據不一致。

第二種,先更新數據庫,再更新緩存

問題:

1、A更新數據庫

2、B更新數據庫

3、B寫入緩存

4、A寫入緩存

出現數據不一致。

考慮另一種情況, 有如下兩點:
(1)如果你是一個寫數據庫場景比較多,而讀數據場景比較少的業務需求,采用這種方案就會導致,數據壓根還沒讀到,緩存就被頻繁的更新,浪費性能。
(2)如果你寫入數據庫的值,並不是直接寫入緩存的,而是要經過一系列復雜的計算再寫入緩存。那么,每次寫入數據庫后,都再次計算寫入緩存的值,無疑是浪費性能的。顯然,刪除緩存更為適合。

第三種,先刪除緩存,再更新數據庫。

問題:

1、A刪除緩存

2、B查詢數據庫獲取舊值

3、B更新了緩存

4、A更新數據庫

出現數據不一致的問題

延時雙刪

public void write(String key,Object data){
	redis.delKey(key);
	db.updateData(data);
	Thread.sleep(1000);
	redis.delKey(key);
}

問題一:延時雙刪,演變成了:先更新數據庫,再刪除緩存。。。。

比如:

1、A刪除緩存

2、B查詢數據庫獲取舊值

3、B更新了緩存

4、A更新數據庫

5、A延時刪緩存

1~3步執行后,數據庫和緩存是一致的,相當於沒刪除。

4~5步:先更新數據庫,再刪緩存。

所以延時雙刪演變成了:先更新數據庫,再刪除緩存。問題還是沒解決。。。

為什么?假設,此時,在第4步執行之前,又來了個查詢C,C查詢到舊值。第6步:C將舊值插入緩存。此時出現緩存和數據庫不一致。

延時並不能解決:C插入緩存的操作在第5步后面執行,比如C遇到網絡問題、GC問題等。當然這是小概率,但並不代表不存在。

當然,延時越長,這個問題越能規避。如果業務需求不是非常嚴格,是可以忽略的。

問題二:吞吐量

問題三:數據庫更新后,無法保證下一次查詢,從緩存獲取的值和數據庫是一致的。

第四種,先更新數據庫,再刪除緩存

問題:上面C的查詢,已經說明問題了。

出現數據不一致的概率,比較小。采取這個方案,取決於業務需求。

終極方案

請求串行化

真正靠譜的方案:將訪問操作串行化

  1. 先刪緩存,將更新數據庫的操作放進有序隊列中
  2. 從緩存查不到的查詢操作,都進入有序隊列

需要解決的問題:

  1. 讀請求積壓,大量超時,導致數據庫的壓力:限流、熔斷
  2. 如何避免大量請求積壓:將隊列水平拆分,提高並行度。
  3. 保證相同請求路由正確。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM