如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致性
首先,分為兩種場景:
一. 針對讀場景:
(1) A請求查詢數(shù)據(jù),如果命中緩存,那么直接取緩存數(shù)據(jù)返回即可。如果請求中不存在,數(shù)據(jù)庫中存在,那么直接取數(shù)據(jù)庫數(shù)據(jù)返回,然后將數(shù)據(jù)同步到Redis中。不會存在數(shù)據(jù)不一致的情況。
(2) 在高并發(fā)的情況下,A請求和B請求一起訪問某條數(shù)據(jù),如果緩存中數(shù)據(jù)存在,直接返回即可,如果不存在,直接取數(shù)據(jù)庫數(shù)據(jù)返回即可。無論A請求B請求誰先誰后,本質上沒有對數(shù)據(jù)進行修改,數(shù)據(jù)本身沒變,只是從緩存中取還是從數(shù)據(jù)庫中取的問題,因此不會存在數(shù)據(jù)不一致的情況。
因此,單獨的讀場景是不會造成Redis與數(shù)據(jù)庫緩存不一致的情況,因此我們不用關心這種情況。
二. 針對寫場景:
(1) 如果該數(shù)據(jù)在緩存中不存在,那么直接修改數(shù)據(jù)庫中的數(shù)據(jù)即可,不會存在數(shù)據(jù)不一致的情況。
(2) 如果該數(shù)據(jù)在緩存中和數(shù)據(jù)庫中都存在,那么就需要既修改緩存中的數(shù)據(jù)又修改數(shù)據(jù)庫中的數(shù)據(jù),而且在高并發(fā)的場景下,還存在修后關系,這就會導致數(shù)據(jù)不一致的問題。
針對(2)的情況有兩個疑問:
(1)是刪除緩存數(shù)據(jù),等待下次查詢該數(shù)據(jù)時,緩存中沒有直接去數(shù)據(jù)庫中查詢,同時添加到緩存中,還是更新緩存呢?
(2)更新緩存中的數(shù)據(jù),是先更新緩存還是先更新數(shù)據(jù)庫呢?
關于疑問(1)有兩個方案
方案1:刪除緩存
優(yōu)點:實現(xiàn)簡單,不需要再更新數(shù)據(jù)庫操作時在進行更新數(shù)據(jù)邏輯,直接刪除對應緩存的key即可。
缺點:由于緩存被刪除,下次查詢無法命中緩存,需要在查詢后將數(shù)據(jù)寫入緩存,增加查詢邏輯。同時在高并發(fā)的情況下,同一時間大量請求訪問該條數(shù)據(jù),第一條查詢請求還未完成寫入緩存操作時,這種情況,大量查詢請求都會打到數(shù)據(jù)庫,加大數(shù)據(jù)庫壓力。
方案2:更新緩存
優(yōu)點:緩存命中率高,只要緩存進行了更新,后續(xù)的讀請求就不會出現(xiàn)緩存未命中的情況。
缺點:在某些業(yè)務場景下,更新數(shù)據(jù)的成本較大,并不是單純將數(shù)據(jù)的數(shù)據(jù)查詢出來丟到緩存中即可,而是需要連接很多張表組裝對應數(shù)據(jù)存入緩存中,并且可能存在更新后,該數(shù)據(jù)并不會被使用到的情況。
綜合分析
在一般的業(yè)務中一般都采用緩存淘汰這種方案,而非緩存更新。因為:
- 大多數(shù)情況下,redis緩存中的數(shù)據(jù)并不是完全復制數(shù)據(jù)庫中的數(shù)據(jù),而是將db中多張表的數(shù)據(jù)進行了重新計算,篩選后更新到redis。如果在db某一張表的數(shù)據(jù)發(fā)生了變化的情況下,需要同步重新計算redis中值的話,更新成本過高。
- 緩存更新后的新值,無法保證一定會有讀請求命中,如果一直沒有請求命中該部分冷數(shù)據(jù),其實是產生了一定的資源浪費(計算成本+存儲成本)。
- 相較于刪除緩存方案來說,僅有一次讀請求cache miss的結果來說,淘汰緩存策略的缺點完全可以容忍。
比如,A表中的字段,1分鐘更改了100次,如果采用更新緩存策略,則需要計算100次,哪怕1分鐘內只有1次讀請求;如果采用淘汰緩存策略,如果1分鐘內只有1次請求,則只需要計算1次即可,開銷大幅度降低。
關于疑問(2)有兩個方案
方案1:先更新緩存,后更新數(shù)據(jù)庫
正常情況
(1)A請求進行寫操作,先淘汰緩存,再更新數(shù)據(jù)庫
(2)B請求進行讀操作,由于A請求已將緩存淘汰,B請求沒有在redis中發(fā)現(xiàn)所需數(shù)據(jù),因此從數(shù)據(jù)庫中讀取數(shù)據(jù),并更新緩存到redis中
異常情況1
(1)A請求進行寫操作,先淘汰緩存
(2)B請求進行讀操作,由于A請求已將緩存淘汰,B請求沒有在redis中發(fā)現(xiàn)所需數(shù)據(jù),因此從數(shù)據(jù)庫中讀取數(shù)據(jù),并更新緩存到redis中。注意,此時redis中被更新的依然是老數(shù)據(jù),A請求的數(shù)據(jù)庫更新操作尚未完成
(3)A請求進行數(shù)據(jù)庫更新操作。此時,數(shù)據(jù)庫中是新數(shù)據(jù),redis緩存中是老數(shù)據(jù),產生了數(shù)據(jù)不一致的問題。且該不一致會一直持續(xù)到緩存自然失效或者下次的更新操作
對于該種異常情況,提供兩種解決思路:
1.異步更新緩存
(1)A請求進行寫操作,先淘汰緩存
(2)B請求進行讀操作,由于A請求已將緩存淘汰,B請求沒有在redis中發(fā)現(xiàn)所需數(shù)據(jù),因此從數(shù)據(jù)庫中讀取數(shù)據(jù)。注意,此時不向redis寫入新的緩存策略
(3)A請求通過訂閱數(shù)據(jù)庫binlog,對redis緩存數(shù)據(jù)進行異步更新
該方案雖然解決了數(shù)據(jù)不一致的問題,但是在數(shù)據(jù)庫更新操作完成前,所有的讀請求都會直接打到數(shù)據(jù)庫上,具有比較大的風險。
2.延時雙刪
(1)A請求進行寫操作,先淘汰緩存
(2)B請求進行讀操作,由于A請求已將緩存淘汰,B請求沒有在redis中發(fā)現(xiàn)所需數(shù)據(jù),因此從數(shù)據(jù)庫中讀取數(shù)據(jù),并更新緩存到redis中。注意,此時redis中被更新的依然是老數(shù)據(jù),A請求的數(shù)據(jù)庫更新操作尚未完成。假設該步驟耗時N秒
(3)A請求進行數(shù)據(jù)庫更新操作。
(4)由于此時redis中寫入了老數(shù)據(jù),因此A請求在休眠M秒后(M略大于N),再次對redis進行淘汰緩存操作
該方案雖然解決了數(shù)據(jù)不一致的問題,但是由于請求A在更新完數(shù)據(jù)庫之后,需要休眠M秒再次淘汰緩存,一定程度上影響了數(shù)據(jù)更新操作的吞吐量??梢試L試將等待M秒更新redis的操作放到另一個單獨的線程(比如消息隊列 + 重試機制)??梢杂行Ь徑馔掏铝拷档偷膯栴}。
異常情況2
(1)A請求進行讀操作,此時redis緩存中沒有數(shù)據(jù),因此直接從數(shù)據(jù)庫中讀取數(shù)據(jù)
(2)B請求進行寫操作,先淘汰緩存,再更新數(shù)據(jù)庫
(3)A請求進行將從數(shù)據(jù)庫中讀到的老數(shù)據(jù),更新到redis。此時產生數(shù)據(jù)不一致問題。
該種異常情況發(fā)生概率極低,一般讀操作比寫操作要快。如有擔心,可以采用上述的延時刪除策略
方案2: 先更新數(shù)據(jù)庫,后更新緩存
正常情況
(1)A請求進行寫操作,先更新數(shù)據(jù)庫,再淘汰緩存
(2)B請求進行讀操作,由于A請求已將緩存淘汰,B請求沒有在redis中發(fā)現(xiàn)所需數(shù)據(jù),因此從數(shù)據(jù)庫中讀取數(shù)據(jù),并更新緩存到redis中
異常情況1
(1)A請求進行寫操作,先更新數(shù)據(jù)庫
(2)B請求進行讀操作,由于A請求尚未淘汰緩存,B請求在redis中發(fā)現(xiàn)所需數(shù)據(jù),因此直接返回老數(shù)據(jù),產生了數(shù)據(jù)不一致的問題
(3)A請求淘汰緩存。
(4)C請求進行讀操作,發(fā)現(xiàn)redis中沒有數(shù)據(jù),因此從數(shù)據(jù)庫中讀取新數(shù)據(jù),并更新至緩存。數(shù)據(jù)不一致的問題解決。
該場景下,數(shù)據(jù)最終一致,只是在高并發(fā)下產生了一小段時間的數(shù)據(jù)不一致。
異常情況2
(1)A請求進行讀操作,此時redis緩存中沒有數(shù)據(jù),因此直接從數(shù)據(jù)庫中讀取數(shù)據(jù)
(2)B請求進行寫操作,更新數(shù)據(jù)庫,并將redis中緩存進行了淘汰(雖然此時redis中并沒有任何的緩存)
(3)A請求將從數(shù)據(jù)庫中讀到的老數(shù)據(jù),更新到redis。此時產生數(shù)據(jù)不一致問題。
該種異常情況發(fā)生概率極低,一般讀操作比寫操作要快。如有擔心,可以采用上述的延時刪除策略。
總結
方案1:先淘汰緩存,后更新數(shù)據(jù)庫的策略,有可能導致長時間的數(shù)據(jù)不一致問題,可以通過延時雙刪 or 異步更新緩存策略進行解決。
方案2:先更新數(shù)據(jù)庫,后更新緩存,有可能導致極短時間內的數(shù)據(jù)不一致,但是數(shù)據(jù)最終是一致的。
以上就是如何保證Redis與數(shù)據(jù)庫的數(shù)據(jù)一致性的詳細內容,更多關于Redis與數(shù)據(jù)庫 數(shù)據(jù)一致性的資料請關注腳本之家其它相關文章!
相關文章
將音頻文件轉二進制分包存儲到Redis的實現(xiàn)方法(奇淫技巧操作)
這篇文章主要介紹了將音頻文件轉二進制分包存儲到Redis的實現(xiàn)方法(奇淫技巧操作),本文通過實例代碼給大家介紹的非常詳細,對大家的學習或工作具有一定的參考借鑒價值,需要的朋友可以參考下2020-07-07解決Redis報錯MISCONF?Redis?is?configured?to?save?RDB?snap
這篇文章主要給大家介紹了關于如何解決Redis報錯MISCONF?Redis?is?configured?to?save?RDB?snapshots的相關資料,文中通過代碼介紹的非常詳細,需要的朋友可以參考下2023-11-11