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


 

問題1:先更新數據庫,再刪除緩存。如果刪除緩存失敗了,那么會導致數據庫中是新數據,緩存中是舊數據,數據就出現了不一致。

解決思路:先刪除緩存,再更新數據庫。如果數據庫更新失敗了,那么數據庫中是舊數據,緩存中是空的,那么數據不會不一致。因為讀的時候緩存沒有,所以去讀了數據庫中的舊數據,然后更新到緩存中。

問題2:數據發生了變更,先刪除了緩存,然后要去修改數據庫,此時還沒修改。一個請求過來,去讀緩存,發現緩存空了,去查詢數據庫,查到了修改前的舊數據,放到了緩存中。隨后數據變更的程序完成了數據庫的修改。完了,數據庫和緩存中的數據不一樣了...

解決思路(1):寫請求先刪除緩存,再去更新數據庫,(異步等待段時間)再刪除緩存(成功表示有臟數據出現)。

這種方案讀取快速,但會出現短時間的臟數據。

解決思路(2):寫請求先修改緩存為指定值,再去更新數據庫,再更新緩存。讀請求過來后,先讀緩存,判斷是指定值后進入循環狀態,等待寫請求更新緩存。如果循環超時就去數據庫讀取數據,更新緩存。

這種方案保證了讀寫的一致性,但是讀請求會等待寫操作的完成,降低了吞吐量

 


詳細參考文章:https://blog.csdn.net/hukaijun/article/details/81010475

 

 

 

 

上篇文章提到,一般存儲數據,都用市面上比較流行的MYSQL和ORACLE數據庫存儲,但隨着數據量的增加,總有一次會達到數據庫性能瓶頸,訪問讀寫速度緩慢。考慮用Redis來緩存數據庫數據,減輕數據庫的壓力,提高訪問請求響應速度。

讀請求流程

先讀緩存數據,緩存數據有,則立即返回結果;如果沒有數據,則從數據庫讀數據,並且把讀到的數據同步到緩存里,提供下次讀請求返回數據。

雖說這樣能減輕數據庫壓力,但是如果修改刪除數據,在多線程高並發的場景下會有可能導致緩存和數據庫數據不一致問題,那該如何解決呢?

分析問題與解決方案

場景一:

問題的根源其實是讀數據與寫數據請求同時並發時,數據庫與緩存數據已更新,但訪問的數據還是老數據,出現了臟讀數據,我們假設讀請求流程沒有問題,那就分析寫請求流程的優化

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

這個方案肯定不行。原因是更新緩存成功,更新數據庫出現異常了,導致緩存數據與數據庫數據完全不一致。

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

這個方案同樣不行,原理跟第一個一樣,數據庫更新成功了,緩存更新失敗,同樣會出現數據不一致問題。

讀請求與寫請求並發

那兩種方案都不行,還有什么更好的解決方案呢?我們遇到寫請求時,可用先刪除緩存數據,再更新數據庫,這樣不管數據庫更新失敗還是緩存刪除失敗,緩存與數據庫始終一致。這種方案一般可滿足上萬人並發操作了,因為刪除緩存到更新數據庫的時間可以用毫秒計算,正常的並發影響不大。但如果是達到上億級訪問,在這時間段內,會出現讀請求在寫請求更新數據庫之前執行,導致數據庫與緩存不一致

場景二:

上億級並發訪問,導致緩存與數據不一致。

比方:淘寶雙11活動,搶購商品,商品數量為100,當前狀態是數據庫和緩存都是100,這時上億賬戶搶購該商品,商品數量要減少。一個消費者A搶購成功,這時應該刪除緩存,更新商品數量,在更新商品數量之前,又有一個消費者B來查看該商品數量,由於緩存清空,到數據庫查詢,該消費者查看到的是商品數量為100,並更新緩存為100,其實商品數量已經被消費者B搶購成功之后,數據庫中商品數量更新為99了,緩存與數據庫數據不一致

方案一:讀寫分離

讀請求只訪問緩存,寫請求只修改數據庫和緩存

讀寫分離流程

寫請求修改數據庫和緩存是事務性動作,如果更新數據庫成功,更新緩存失敗,則回滾數據庫,保證緩存與數據庫數據強一致。這樣實現了讀寫分離,不僅提高了讀的響應速度,由寫請求負責緩存與數據庫一致,只有寫請求成功才會影響到緩存的內容,時效性大大增強。

方案二:隊列存儲請求

方案二流程

沿用場景一的解決方案,為解決其缺陷,添加隊列,凡是遇到寫請求,則將寫請求放入隊列中,由隊列對寫請求統一管理,寫請求處理成功,則從隊列中刪除。當有一個讀請求過來時,到隊列查詢,是否有對應的寫請求,如果有則放入隊列中,等待寫請求執行完之后再執行讀請求。為防止某個請求阻塞情況,為其設置超時機制或者過期機制。

這種方案雖可行,但是倘若訪問量大,處理器來不及處理,隊列內的請求數量越來越高,則會影響查詢效率。出現這種情況,就要加機器集群執行,幫忙分擔壓力

 

 


免責聲明!

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



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