緩存數據庫雙寫一致性問題


引言

在引入緩存系統的項目中,我們需要舊數據進行更新操作時,我們是先淘汰緩存,再更新數據庫。還是先更新數據庫,再淘汰緩存。亦或是更新數據庫,再更新緩存呢?下面,將會講講小編對這三種方案的優缺點的一些想法。

 

目的

  1. 整理自己對這方面的知識;
  2. 分享自己的看法,和小伙伴們一起學習;
  3. 用初學者的角度來淺顯的講解這方面的內容。

 

緩存更新策略

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

 

先更新數據庫,再更新緩存

這套方案,小編認為大多數場景不合適。為什么呢?主要從以下幾個原因講解:

 

一、資源浪費

在一些大型的信息網站中(博客、貼吧),我們引入緩存主要是對熱數據(請求頻繁的)進行緩存,而這時候,如果很多用戶對於冷數據(長時間沒人訪問,或者訪問量很少)進行更新,然后再去更新緩存,這就造成了緩存資源的大量浪費(因為訪問量少,導致這些緩存命中低,浪費緩存資源)。

 

二、臟數據

這是由於出現了並發操作的原因導致的,如:同時有兩個請求A和B對數據進行了更新操作,由於網絡原因,可能存在以下情況:

  1. 請求A更新了數據庫;
  2. 請求B更新了數據庫;
  3. 請求B更新了緩存;
  4. 請求A更新了緩存。

這就出現了A數據覆蓋了B數據的情況,此時就產生了臟數據,如果沒有緩存定時過期機制,此時的臟數據需要等待下一次的更新,才會對緩存進行更新,雖然用戶看到數據出現問題,會再重新更新一次,但這已經有多了一次不必要的請求了,寫請求量大的時候,容易造成眾多不必要的更新請求。

 

三、請求時間

如果緩存不是一種簡單的數據緩存,而是需要經過較為復雜的運算,才能得出緩存值,這時候,請求將會在計算緩存值上,耗費一部分時間,而這就導致了請求的響應時間變長,增加系統的負擔,降低了系統的處理能力。

 

四、頻繁寫入

在寫請求很多,而讀請求很少的場景下,緩存沒起到多大的作用,就給頻繁更新了,造成了資源浪費,如:

  1. 對數據A進行了一次修改,生成了緩存A;
  2. 此時沒有讀取數據A的請求;
  3. 對數據A進行了一次修改,更新了緩存A;
  4. 此時沒有讀取數據A的請求;
  5. 對數據A進行了一次修改;
  6. 此時有了讀取數據A的請求。

這時就會造成緩存的不必要更新操作(沒有人讀取緩存),用戶量大的時候,會造成大量的不必要操作,造成系統資源的浪費。

 

適合場景

當然,這種也不是說就不能使用這些,既然存在,就有其存在的道理。一下的場景就適合:

  1. 讀請求占據網站的總流量的99%;
  2. 網站數據量不大(幾十萬的文章數據);
  3. 很少會去更新數據(一般文章寫好后,不會去更新)。

 

案例

  1. 個人博客
  2. 手冊網站(w3cschool、菜鳥教程等)

 

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

這種策略比較多平台使用,如:Facebook。但是這種策略也存在一些問題,如:

 

一、臟數據

造成臟數據的原因主要由並發引起,如:

  1. 用戶A請求數據A
  2. 數據A緩存失效
  3. 用戶A從數據庫中得到舊數據數據A
  4. 用戶B更新了數據A(新數據)
  5. 用戶B刪除了緩存
  6. 用戶A將查到舊數據寫入了緩存

此時就產生了臟數據,雖然這種概率非常小,但對於更新不頻繁的網站來說,此時的臟數據就是個很嚴重的錯誤。

 

二、緩存刪除失敗

  1. 用戶A更新了數據A
  2. 用戶A刪除數據A的緩存失敗
  3. 用戶B讀到數據A緩存的舊數據

此時就產生了數據不一致的問題。

 

解決方案

1、設置緩存的有效時間(最簡單的方案)

優點:

  • 簡單
  • 易操作

缺點:

  • 會存在短時間內的舊數據
  • 如果數據量太多,緩存有效時間短,容易發生一段時間內緩存大量失效,此時的數據庫壓力突然劇增,引發緩存雪崩現象(緩存有效時間為隨機值減少發生緩存雪崩的可能性)

 

2、消息隊列(比較復雜,需要引入消息隊列系統)

步驟:

  1. 更新數據庫;
  2. 刪除緩存失敗;
  3. 將需要刪除的Key發送到消息隊列;
  4. 隔斷時間從消息隊列中拉取要刪除的key;
  5. 繼續刪除,直至成功為止。

優點:

  • 不會引發緩存雪崩
  • 只刪除需要刪除的緩存

缺點:

  • 引入了消息系統(增加了系統的復雜性)

 

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

這種方法也是比較多人使用的,但是也會出現臟數據的問題:

 

原因

  1. 用戶A刪除緩存失敗
  2. 用戶A成功更新了數據

  或者

  1. 用戶A刪除了緩存;
  2. 用戶B讀取緩存,緩存不存在;
  3. 用戶B從數據庫拿到舊數據;
  4. 用戶B更新了緩存;
  5. 用戶A更新了數據。

以上兩種情況都能造成臟數據的產生。

 

解決方案

1、設置緩存的有效時間(最簡單的方案)

優點:

  • 簡單
  • 易操作

缺點:

  • 會存在短時間內的舊數據
  • 如果數據量太多,緩存有效時間短,容易發生一段時間內緩存大量失效,此時的數據庫壓力突然劇增,引發緩存雪崩現象(緩存有效時間為隨機值減少發生緩存雪崩的可能性)

 

2、消息隊列

  1. 先淘汰緩存;
  2. 更新數據庫;
  3. 將需要淘汰的緩存Key發送到消息隊列;
  4. 另起一程序拉取消息隊列的數據;
  5. 對需要刪除的key進行刪除,直至刪除為止。

優點:

  • 保證了緩存的刪除
  • 不會增加更新的處理時間
  • 不會引發緩存雪崩

缺點:

  • 會增加一次緩存miss(可以忽略不計)
  • 引入了消息系統(增加了系統的復雜性)

 

總結

  1. 上述的方法根據自己的系統業務來選擇;
  2. 既然存在,就有其道理;
  3. 使用緩存有效時間時,需要注意緩存雪崩問題;
  4. 可以引入消息系統來避免臟數據;
  5. 還存在分析binlog來異步刪除緩存(小編未研究這個,所以小編沒寫);


免責聲明!

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



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