如何保證緩存和數據庫的雙寫一致性

前言

在項目中緩存是經常用到的,爲了減少和數據庫的交互,小夥伴們利用緩存的思路如下:

你知道如何更新緩存嗎?如何保證緩存和數據庫雙寫一致性?

緩存設計思路

我們小夥伴們有沒有考慮到緩存更新的問題,小夥伴們肯定會說肯定用過啊,有數據更新時,把緩存清空掉就行了啊,下一次訪問的時候服務就會把新值設置到緩存中了。這樣不就行了嗎?對的,在一般項目中,這樣的使用就夠了。那在高併發場景下,會有什麼問題?

我們舉例說明,就拿商品的庫存作爲緩存。那現在我們要更新緩存中的庫存值,怎麼進行操作,我們看下面幾個場景:

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

存在的問題場景:請求A更新值爲99,請求B更新值爲98

你知道如何更新緩存嗎?如何保證緩存和數據庫雙寫一致性?

 

上圖流程:

1)請求A先發起,更新數據庫爲99,但還沒有來得及更新緩存
2)請求B發起,更新數據庫爲98,又更新了緩存值爲98
3)請求A這時才更新緩存的值爲99

這樣數據庫的值爲98,但緩存的值爲99,數值不一致。(不推薦)

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

這個流程跟上面很類似,出現的問題也很類似

1)請求A先更新緩存爲99,但還沒有來得及更新數據庫
2)請求B更新緩存爲98,又更新了數據庫爲98
3)請求A這時更新數據庫爲99

這樣就緩存的值爲98,數據庫爲99導致不一致。(不推薦)

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

存在的問題場景:請求A更新值爲99,請求B獲取值

你知道如何更新緩存嗎?如何保證緩存和數據庫雙寫一致性?

 

上圖中請求流程:

1)請求A更新值,先把緩存中的值刪除,但還沒有來得及更新數據庫
2)此時請求B過來查詢此值,發現緩存中不存在,就到數據庫中查詢
3)請求B在數據庫中獲取到值,在把值設置到緩存中。
4)請求A這時才更新數據庫裏面的值爲99

這樣就導致了緩存和數據庫的不一致問題,緩存中的值一直是舊數據。(不推薦)

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

這個方案也是老外提出的《Cache-Aside pattern》更新緩存的策略。這種策略先保證了源頭的數據一定是正確的。這種策略是不是萬無一失呢,有一種非常特殊的場景

你知道如何更新緩存嗎?如何保證緩存和數據庫雙寫一致性?

 

上圖流程:建立中緩存突然失效了

1)請求A發起查詢請求,直接到數據庫查詢到100,但還沒有來得及去設置緩存
2)請求B更新值,先更新數據庫,在刪除緩存
3)請求A這時才設置緩存爲100

這種情況發生的不一致,是因爲緩存突然失效了。而且還要保證請求B更新操作 比 請求A的查詢操作還要快;纔會導致不一致。這種情況概率會很少。一般要求不高的項目可以採用此方式(推薦)。

緩存刪除失敗,導致不一致

這種先更新數據庫,再刪除緩存的策略中,因爲要刪除緩存,但如果緩存刪除失敗,就會導致數據庫與緩存不一致。這個問題怎麼辦?我們正常想到的是利用我們MQ中間件去實現。

你知道如何更新緩存嗎?如何保證緩存和數據庫雙寫一致性?

 

上圖的流程,如果刪除緩存失敗,發送消息投遞到消息中間件中,進入消息隊列。也許有小夥伴就會問,如果消息投遞失敗怎麼辦?我們可以利用消息中間件那邊的保證100%消息投遞的機制。這樣就保證了即使刪除消息失敗,我們也會重試。

不過這個方案有個問題,就是和我們應用服務的業務代碼耦合的比較厲害。代碼業務不清晰。

那我們有沒有別的方案呢,對業務沒有侵入呢?

你知道如何更新緩存嗎?如何保證緩存和數據庫雙寫一致性?

 

上圖中其實是利用了mysql的底層機制,binlog日誌進行刪除緩存,這樣就不需要和業務關聯,刪除緩存服務是獨立的。我們可以利用阿里開源的canal去操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章