分佈式之數據庫和緩存雙寫一致性方案解析

一.爲何寫這邊文章redis

首先,緩存因爲其高併發和高性能的特性,已經在項目中被普遍使用。在讀取緩存方面,你們沒啥疑問,都是按照下午的流程來進行業務操做:數據庫

clipboard.png
可是,在更新緩存方面,對於更新完數據庫,是更新緩存呢,仍是刪除緩存?又或者是先刪除緩存,再更新數據庫?其實這一塊是存在很大的爭議。緩存

2、文章結構網絡

  1. 講解緩存更新策略;
  2. 對每種策略進行缺點分析;
  3. 針對缺點給出改進方案;

3、正文併發

先作一個說明,從理論上來講,給緩存設置過時時間,是保證最終一致性的解決方案。這種方案下,咱們能夠對緩存的數據設置過時時間,全部的寫操做以數據庫爲準,對緩存操做只是盡最大努力便可。也就是說若是數據庫寫成功,緩存更新失敗,那麼只要到達過時時間,則後面的讀請求天然會從數據庫中讀取新值,而後回填緩存。所以,接下來討論的思路不依賴於給緩存設置過時時間這個方案。在這裏,咱們討論三種更新策略:高併發

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

爲何沒有先更新緩存,再更新數據庫這種策略?答案不用說了吧。性能

4、先更新數據庫,再更新緩存spa

這套方案,你們是廣泛反對的,爲何呢?有以下兩點緣由:線程

緣由一:線程安裝角度
同時又請求A和請求B進行更新操做,那麼會出現:code

  1. 線程A更新了數據庫
  2. 線程B更新了數據庫
  3. 線程B更新了緩存
  4. 線程A更新了緩存

這就出現請求A更新緩存應該比請求B更新緩存早纔對,可是由於網絡等緣由,B比A更早更新了緩存。這就致使了髒數據,所以不考慮!

緣由2、業務場景角度
有以下兩點:
1)若是你是一個寫數據庫場景比較多,而讀數據場景比較少的業務需求,採用這種方案就會致使,數據壓根還沒讀到,緩存就被頻繁的更新,浪費性能。

2)若是你寫入數據庫的值,並非直接寫入緩存的,而是要通過一系列負責的計算再寫入緩存。那麼,每次寫入數據庫後,都要再次計算寫入緩存的值,無疑是浪費性能的。顯然,刪除緩存更爲合適。

接下來討論的就是爭議最大的,先刪除緩存,再更新數據庫。仍是先更新數據庫,再刪除緩存的問題。

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

該方案會致使不一致的緣由:同時有一個請求A進行更新操做,另外一個請求B進行查詢操做。那麼就會出現如下情形:

  • 請求A進行寫操做,刪除緩存
  • 請求B查詢發現緩存不存在
  • 請求B去數據庫查詢獲得舊值
  • 請求B將舊值寫入緩存
  • 請求A將新值寫入數據庫

上述狀況就會致使不一致的請求出現。並且,若是不採用給緩存設置過時時間策略,該數據永遠都是髒數據。
那麼,該如何解決呢?採用延時雙刪除策略!僞代碼以下:

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

解釋一下:

  • 先淘汰緩存
  • 再寫數據庫(這兩步和原來同樣)
  • 休眠1秒,再次淘汰緩存

這麼作,能夠將1秒內所形成的緩存髒數據,再次刪除!

相關文章
相關標籤/搜索