保證緩存和數(shù)據(jù)庫的數(shù)據(jù)一致性詳解
1、錯誤的解決方案
1.1、 先更新數(shù)據(jù)庫,再刪除緩存
若數(shù)據(jù)庫更新成功,刪除緩存操作失敗,則此后讀到的都是緩存中過期的數(shù)據(jù),造成不一致問題。
1.2、 先更新數(shù)據(jù)庫,再更新緩存
同刪除緩存策略一樣,若數(shù)據(jù)庫更新成功緩存更新失敗則會造成數(shù)據(jù)不一致問題。
1.3、 先刪除緩存,再更新數(shù)據(jù)庫

1.4、 先更新緩存,再更新數(shù)據(jù)庫
若緩存更新成功數(shù)據(jù)庫更新失敗, 則此后讀到的都是未持久化的數(shù)據(jù)。因為緩存中的數(shù)據(jù)是易失的,這種狀態(tài)非常危險。

2、正確的解決方案
2.1、使用 CAS
CAS (Check-And-Set 或 Compare-And-Swap)是一種常見的保證并發(fā)安全的手段。CAS 當且僅當客戶端最后一次取值后該 key 沒有被其他客戶端修改的情況下,才允許當前客戶端將新值寫入。
func CAS(oldVal, newVal) {
if cache.get() == oldVal {
cache.set(newVal)
}
}

- 目前一些兼容 Redis 協(xié)議的中間件已經提供了 CAS 命令的支持,比如阿里的 Tair 以及騰訊的 Tendis。
- Redis 官方本身是不支持CAS的操作,但是我們可以通過WATCH 和MULTI 命令實現(xiàn)類似的效果
- WATCH 命令用于監(jiān)視一個或多個鍵的變化,并在某個鍵被修改后取消事務,從而確保事務的原子性
- MULTI 命令用于開始一個事務,將多個命令打包成一個事務,然后一次性執(zhí)行。如果在執(zhí)行事務期間有其他客戶端對事務中的鍵進行修改,那么事務會被取消
2.2、使用分布式鎖
CAS 假設發(fā)生并發(fā)問題的概率不大, 所以 CAS 也被稱為樂觀鎖。那么悲觀鎖能否解決我們的問題呢?
還是以「先更新數(shù)據(jù)庫,再更新緩存」方案中兩個寫線程競爭為例, 我們要求任何線程在寫入或讀取數(shù)據(jù)庫前都需要獲取排它鎖。

分布式鎖同樣可以解決并發(fā)問題,只是成本可能略高。
2.3、使用消息隊列異步更新
使用消息隊列實現(xiàn)異步更新時,可以將緩存更新的請求發(fā)送到消息隊列中,由消息隊列異步地處理緩存更新操作。下面是一個簡單的案例:
假設有一個電商網(wǎng)站,需要對商品信息進行緩存。當用戶訪問商品詳情頁面時,先從緩存中讀取商品信息,如果緩存中沒有,則從數(shù)據(jù)庫中讀取。
- 當商品信息發(fā)生變化時,需要更新緩存中的數(shù)據(jù)。這時可以通過消息隊列異步更新緩存,具體步驟如下:
- 當商品信息發(fā)生變化時,先更新數(shù)據(jù)庫中的數(shù)據(jù)。
- 將商品信息更新請求發(fā)送到消息隊列中。
- 消息隊列異步地處理緩存更新操作,讀取最新的商品信息,并將其更新到緩存中。
這樣就可以保證緩存中的數(shù)據(jù)是最新的,避免了因為緩存中的數(shù)據(jù)過期而導致的數(shù)據(jù)不一致問題。同時,使用消息隊列可以提高更新的可靠性和性能,避免因為緩存更新失敗而導致的數(shù)據(jù)庫和緩存數(shù)據(jù)不一致問題。
為什么異步更新可以解決
- 異步更新緩存:當商品信息發(fā)生變化時,先更新數(shù)據(jù)庫中的數(shù)據(jù),然后將緩存更新請求發(fā)送到消息隊列中,由消息隊列異步地處理緩存更新操作。這樣,即使緩存更新失敗,也不會影響數(shù)據(jù)庫中的數(shù)據(jù),僅僅是緩存中的數(shù)據(jù)不是最新的而已。
- 消息隊列的可靠性:消息隊列通常具有高可靠性和高可用性,可以保證消息的可靠傳輸和處理。即使在消息隊列出現(xiàn)故障的情況下,也可以通過消息隊列的備份、重試等機制來保證消息的可靠性。因此,即使緩存更新失敗,也可以通過重試等機制來保證緩存最終被更新。
如果通過異步更新,更新緩存還是失敗了怎么辦
- 重試更新緩存:當緩存更新失敗時,可以嘗試重新更新緩存。可以設置重試次數(shù)和重試間隔時間,避免因為頻繁重試而影響性能。
- 回滾數(shù)據(jù)庫更新:當緩存更新失敗時,可以回滾數(shù)據(jù)庫中的更新操作,保證數(shù)據(jù)庫和緩存中的數(shù)據(jù)一致。但是,回滾操作可能會影響數(shù)據(jù)庫中的其他操作,需要考慮到這個問題。
- 延遲更新緩存:當緩存更新失敗時,可以將緩存更新請求放入一個延遲隊列中,一段時間后再次嘗試更新緩存。這樣可以避免頻繁重試而影響性能,同時保證緩存最終被更新。
- 使用讀寫分離:將讀請求和寫請求分別處理,讀請求從緩存中讀取數(shù)據(jù),寫請求先更新數(shù)據(jù)庫,再更新緩存。這樣可以避免因為緩存更新失敗而導致的數(shù)據(jù)不一致問題。
2.4、將數(shù)據(jù)庫更新和緩存更新放在同一個事務中
可以保證在事務執(zhí)行成功時,數(shù)據(jù)庫和緩存中的數(shù)據(jù)都被更新;在事務執(zhí)行失敗時,數(shù)據(jù)庫和緩存中的數(shù)據(jù)都不會被更新,保證了數(shù)據(jù)的一致性。
- 要將MySQL和Redis放入同一個事務中,需要使用分布式事務處理框架,如XA或TCC。這些框架可以確保在整個事務過程中,MySQL和Redis的操作都能夠得到正確的協(xié)調和同步。
- XA:XA是一種分布式事務處理標準,它可以確保在多個數(shù)據(jù)庫之間進行事務處理時,所有的操作都能夠得到正確的協(xié)調和同步。在MySQL和Redis中都有XA實現(xiàn),可以通過XA接口實現(xiàn)分布式事務。
- TCC:TCC是一種補償性事務處理框架,它通過預留資源、確認資源和釋放資源三個步驟來實現(xiàn)分布式事務。在MySQL和Redis中都有TCC實現(xiàn),可以通過TCC接口實現(xiàn)分布式事務。
- 需要注意的是,使用分布式事務框架會增加系統(tǒng)的復雜性和開銷,需要仔細考慮是否真正需要在MySQL和Redis之間實現(xiàn)分布式事務如果可以接受稍微降低一些數(shù)據(jù)一致性的風險,可以使用其他技術來實現(xiàn)MySQL和Redis之間的數(shù)據(jù)同步,如消息隊列、定時任務等。
到此這篇關于保證緩存和數(shù)據(jù)庫的數(shù)據(jù)一致性詳解的文章就介紹到這了,更多相關緩存和數(shù)據(jù)庫數(shù)據(jù)一致性內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java8新特性之重復注解(repeating annotations)淺析
這篇文章主要介紹了Java8新特性之重復注解(repeating annotations)淺析,這個新特性只是修改了程序的可讀性,是比較小的一個改動,需要的朋友可以參考下2014-06-06
Spring中的FactoryBean與BeanFactory詳細解析
這篇文章主要介紹了Spring中的FactoryBean與BeanFactory詳細解析,在Spring框架中,FactoryBean和BeanFactory是兩個關鍵的接口,用于創(chuàng)建和管理對象實例,它們在Spring的IoC(Inversion of Control,控制反轉)容器中發(fā)揮著重要的作用,需要的朋友可以參考下2023-11-11
idea?compile項目正常啟動項目的時候build失敗報“找不到符號”等問題及解決方案
這篇文章主要介紹了idea?compile項目正常,啟動項目的時候build失敗,報“找不到符號”等問題,這種問題屬于lombok編譯失敗導致,可能原因是依賴jar包沒有更新到最新版本,需要的朋友可以參考下2023-10-10
Spring?Cloud?Alibaba實現(xiàn)服務的無損下線功能(案例講解)
這篇文章主要介紹了Spring?Cloud?Alibaba實現(xiàn)服務的無損下線功能?,本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2023-03-03

