Redis與數(shù)據(jù)庫數(shù)據(jù)一致性的原因及解決方案
一、概述
redis是一種開源、使用內(nèi)存存儲數(shù)據(jù)介質(zhì)的鍵值對存儲系統(tǒng)。redis的讀寫速度非???,常用于應(yīng)用與數(shù)據(jù)庫之間做緩存層,能夠減少數(shù)據(jù)庫IO操作,提升數(shù)據(jù)庫性能,并提高應(yīng)用端的請求響應(yīng)速度。但涉及到并發(fā)讀寫數(shù)據(jù)時就容易出現(xiàn)redis與數(shù)據(jù)庫數(shù)據(jù)一致性的問題。
二、原因
應(yīng)用對數(shù)據(jù)庫的操作無外乎兩個操作讀操作和寫操作。 redis作為應(yīng)用與數(shù)據(jù)庫之間的緩存層,通常應(yīng)用在操作數(shù)據(jù)庫之前先操作redis,讀操作時只有redis不存在,才會操作數(shù)據(jù)庫,寫操作時需要更新數(shù)據(jù)庫和redis緩存。針對這兩個操作流程,我們需要分析下在什么場景下才會出現(xiàn)redis與數(shù)據(jù)庫數(shù)據(jù)不一致的問題。
1、讀取數(shù)據(jù)
讀取數(shù)據(jù)流程如下:
1. 應(yīng)用程序需要從數(shù)據(jù)庫讀取數(shù)據(jù)時,先查詢redis的緩存數(shù)據(jù)是否命中。
2. 若命中,直接返回。若未命中,再去查詢數(shù)據(jù)庫。
3. 將查詢到的數(shù)據(jù)先保存到redis中,并設(shè)置過期時間,再將數(shù)據(jù)返回到應(yīng)用。
以上是常用的一個讀取數(shù)據(jù)的場景,根據(jù)場景分析,只讀的情況下是不會出現(xiàn)redis與數(shù)據(jù)庫數(shù)據(jù)不一致的情況。
2、寫數(shù)據(jù)
寫數(shù)據(jù)流程一般操作流程可以分為以下4種:
1. 先更新緩存再更新數(shù)據(jù)庫。
2. 先刪除緩存再更新數(shù)據(jù)庫。
3. 先更新數(shù)據(jù)庫再更新緩存。
4. 先更新數(shù)據(jù)庫再刪除緩存。
根據(jù)以上4種流程分析,可以明確出兩個問題,一個是緩存是更新還是刪除,另外一個是先操作數(shù)據(jù)庫還是先操作緩存呢。
2.1、緩存是更新還是刪除
推薦使用刪除緩存。因?yàn)榫彺娴母鲁杀咎摺?/strong>由于大多數(shù)情況下數(shù)據(jù)并不是直接寫入緩存的,需要經(jīng)過一系列復(fù)雜的計(jì)算再寫入緩存的。若采用更新方式,那么每次寫入數(shù)據(jù)庫后,都需計(jì)算寫入緩存的值,無疑是浪費(fèi)性能的。刪除緩存操作簡單,副作用只是增加了一次cache miss,建議使用刪除策略。
2.2 先操作數(shù)據(jù)庫還是先操作緩存
2.2.1 先操作緩存
先操作緩存的流程如下:
先操作緩存的流程,就是先將緩存中數(shù)據(jù)刪除,再更新數(shù)據(jù)庫。
數(shù)據(jù)不一致
在讀寫并發(fā)操作的情況下,如何出現(xiàn)的數(shù)據(jù)不一致的問題呢。先看下并發(fā)流程:
1. 線程1發(fā)起修改數(shù)據(jù)請求,會進(jìn)行刪除緩存操作。
2. 接著更新數(shù)據(jù)庫時出現(xiàn)了網(wǎng)絡(luò)延遲。
3. 線程1由于網(wǎng)絡(luò)延遲還未對數(shù)據(jù)庫進(jìn)行修改,此時線程2執(zhí)行查詢請求,會去緩存中查詢數(shù)據(jù)。
4. 線程2在緩存中未查詢到數(shù)據(jù),再去查詢數(shù)據(jù)庫。
5. 線程2將查詢到數(shù)據(jù)舊數(shù)據(jù)放到緩存中,并將數(shù)據(jù)返回。
6. 線程1在線程2數(shù)據(jù)查詢完成后,才對數(shù)據(jù)庫進(jìn)行了修改。
在這個過程中就出現(xiàn)了redis與數(shù)據(jù)庫數(shù)據(jù)不一致的問題,只有等redis中數(shù)據(jù)過期時間到了,才能將新數(shù)據(jù)更新到緩存中。
2.2.2 先操作數(shù)據(jù)庫
先操作數(shù)據(jù)庫的流程如下:
先操作數(shù)據(jù)庫的流程,就是先對數(shù)據(jù)庫進(jìn)行修改,再將緩存中的數(shù)據(jù)進(jìn)行刪除。
數(shù)據(jù)不一致
在讀寫并發(fā)操作的情況下,如何出現(xiàn)的數(shù)據(jù)不一致的問題呢。
1. 線程1發(fā)起修改數(shù)據(jù)請求,先更新數(shù)據(jù)庫。
2. 線程2在線程1更新數(shù)據(jù)庫期間,發(fā)起查詢請求,從緩存中獲取到舊數(shù)據(jù)(臟數(shù)據(jù))。
3. 線程1完成數(shù)據(jù)庫更新后,刪除緩存中的數(shù)據(jù)。
在這個過程中出現(xiàn)了短暫的數(shù)據(jù)不一致,但redis和數(shù)據(jù)庫數(shù)據(jù)是最終一致性的。所以推薦先操作數(shù)據(jù)庫再操作緩存。
通過上述原因分析,可以得出在并發(fā)的讀寫情況下,正常使用redis與數(shù)據(jù)庫不管是先操作redis還是先操作數(shù)據(jù)庫,可能都會數(shù)據(jù)不一致問題。
注意:由于redis和數(shù)據(jù)庫操作不是原子的,若在redis和數(shù)據(jù)庫之間加鎖是可以實(shí)現(xiàn)數(shù)據(jù)一致,但也違背了使用redis的初衷。
二、解決方案
在不考慮redis操作失敗的情況下,保證redis與數(shù)據(jù)庫數(shù)據(jù)一致性的解決方案有4種。
1、延遲刪除機(jī)制
該機(jī)制是在數(shù)據(jù)庫數(shù)據(jù)更新后,先延遲一段時間后再次刪除緩存數(shù)據(jù)。線程1寫請求,線程2查詢請求,通過延遲雙刪機(jī)制保證redis與數(shù)據(jù)庫數(shù)據(jù)一致性。
通過(6)步延遲一段時間后再進(jìn)行redis的刪除,在并發(fā)讀寫情況下保證redis與數(shù)據(jù)庫數(shù)據(jù)一致性。具體延遲多長時間,需評估項(xiàng)目讀數(shù)據(jù)業(yè)務(wù)邏輯耗時(即線程2從數(shù)據(jù)庫讀取數(shù)據(jù)到更新緩存成功的時間)。確保查詢請求結(jié)束,更新請求可以刪除查詢請求造成的緩存臟數(shù)據(jù)。
2、binlog同步刪除機(jī)制
通過canal組件訂對binlog日志進(jìn)行訂閱,模仿數(shù)據(jù)庫的slave數(shù)據(jù)庫的備份請求,使得redis緩存數(shù)據(jù)刪除,保證redis與數(shù)據(jù)庫數(shù)據(jù)一致性。
通過上面兩種方式,在并發(fā)讀寫的情況下保證redis與數(shù)據(jù)庫數(shù)據(jù)最終一致性。但可能存在redis刪除失敗的情況,一旦出現(xiàn)就會有redis與數(shù)據(jù)庫數(shù)據(jù)不一致的問題。只有等redis中數(shù)據(jù)過期時間到了,才能將新數(shù)據(jù)更新到緩存中。
3、異步重試刪除機(jī)制
一旦緩存刪除失敗,可以通過重試機(jī)制設(shè)置重試次數(shù)保證一定刪除成功。如重試3次,三次操作都失敗則記錄日志并發(fā)送告警,通知技術(shù)人員進(jìn)行人工介入處理。在高并發(fā)環(huán)境下,重試最好使用異步方式,可以通過MQ實(shí)現(xiàn)這種機(jī)制。
通過(6)步延遲刪除緩存數(shù)據(jù)時,刪除時失敗,緩存中存儲的還是臟數(shù)據(jù)(舊數(shù)據(jù))。線程1的應(yīng)用作為producer異步發(fā)送需要刪除key到MQ。線程1的應(yīng)用監(jiān)聽MQ,重試刪除操作。
通過重試刪除機(jī)制,可以能夠保證redis緩存一定能刪除成功,保證redis與數(shù)據(jù)庫數(shù)據(jù)一致性。但這種方式對業(yè)務(wù)代碼造成侵入,代碼過于耦合。
4、binlog解耦異步重試機(jī)制
可以使用阿里巴巴開源框架canal來實(shí)現(xiàn)程序解耦。通過利用canal提供的java客戶端,監(jiān)聽canal通知消息。當(dāng)java客戶端(項(xiàng)目)收到binlog變化的消息時,完成對緩存的處理。
數(shù)據(jù)庫更新后,canal訂閱binlog日志,將變更的數(shù)據(jù)發(fā)送消息通知給java客戶端(spring boot項(xiàng)目)。java客戶端執(zhí)行延遲刪除緩存,若刪除失敗,java客戶端作為producer異步發(fā)送需要刪除key的MQ消息進(jìn)行重試??蛻舳吮O(jiān)聽MQ消息,執(zhí)行重試刪除緩存操作。
三、總結(jié)
通過上述分析我們知道造成redis與數(shù)據(jù)庫數(shù)據(jù)不一致的問題主要在于并發(fā)情況下,讀寫并發(fā)操作可能會出現(xiàn)這個問題。通過4中解決方案,能夠很好的解決redis與數(shù)據(jù)庫數(shù)據(jù)一致性問題。
到此這篇關(guān)于Redis與數(shù)據(jù)庫數(shù)據(jù)一致性的原因及解決方案的文章就介紹到這了,更多相關(guān)Redis與數(shù)據(jù)庫數(shù)據(jù)一致性解決內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Redis高級數(shù)據(jù)類型Hyperloglog、Bitmap的使用
很多小伙伴在面試中都會被問道 Redis的常用數(shù)據(jù)結(jié)構(gòu)有哪些?可能很大一部分回答都是 string、hash、list、set、zset,但其實(shí)還有Hyperloglog和Bitmap,本文就來介紹一下2021-05-05redis配置standAlone版的jedisPool示例
這篇文章主要為大家介紹了redis配置standAlone版的jedisPool示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07