Redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性的原因及解決方案
一、概述
redis是一種開源、使用內(nèi)存存儲(chǔ)數(shù)據(jù)介質(zhì)的鍵值對(duì)存儲(chǔ)系統(tǒng)。redis的讀寫速度非??欤S糜趹?yīng)用與數(shù)據(jù)庫(kù)之間做緩存層,能夠減少數(shù)據(jù)庫(kù)IO操作,提升數(shù)據(jù)庫(kù)性能,并提高應(yīng)用端的請(qǐng)求響應(yīng)速度。但涉及到并發(fā)讀寫數(shù)據(jù)時(shí)就容易出現(xiàn)redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性的問(wèn)題。
二、原因
應(yīng)用對(duì)數(shù)據(jù)庫(kù)的操作無(wú)外乎兩個(gè)操作讀操作和寫操作。 redis作為應(yīng)用與數(shù)據(jù)庫(kù)之間的緩存層,通常應(yīng)用在操作數(shù)據(jù)庫(kù)之前先操作redis,讀操作時(shí)只有redis不存在,才會(huì)操作數(shù)據(jù)庫(kù),寫操作時(shí)需要更新數(shù)據(jù)庫(kù)和redis緩存。針對(duì)這兩個(gè)操作流程,我們需要分析下在什么場(chǎng)景下才會(huì)出現(xiàn)redis與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致的問(wèn)題。
1、讀取數(shù)據(jù)
讀取數(shù)據(jù)流程如下:

1. 應(yīng)用程序需要從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)時(shí),先查詢r(jià)edis的緩存數(shù)據(jù)是否命中。
2. 若命中,直接返回。若未命中,再去查詢數(shù)據(jù)庫(kù)。
3. 將查詢到的數(shù)據(jù)先保存到redis中,并設(shè)置過(guò)期時(shí)間,再將數(shù)據(jù)返回到應(yīng)用。
以上是常用的一個(gè)讀取數(shù)據(jù)的場(chǎng)景,根據(jù)場(chǎng)景分析,只讀的情況下是不會(huì)出現(xiàn)redis與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致的情況。
2、寫數(shù)據(jù)
寫數(shù)據(jù)流程一般操作流程可以分為以下4種:
1. 先更新緩存再更新數(shù)據(jù)庫(kù)。
2. 先刪除緩存再更新數(shù)據(jù)庫(kù)。
3. 先更新數(shù)據(jù)庫(kù)再更新緩存。
4. 先更新數(shù)據(jù)庫(kù)再刪除緩存。
根據(jù)以上4種流程分析,可以明確出兩個(gè)問(wèn)題,一個(gè)是緩存是更新還是刪除,另外一個(gè)是先操作數(shù)據(jù)庫(kù)還是先操作緩存呢。
2.1、緩存是更新還是刪除
推薦使用刪除緩存。因?yàn)榫彺娴母鲁杀咎摺?/strong>由于大多數(shù)情況下數(shù)據(jù)并不是直接寫入緩存的,需要經(jīng)過(guò)一系列復(fù)雜的計(jì)算再寫入緩存的。若采用更新方式,那么每次寫入數(shù)據(jù)庫(kù)后,都需計(jì)算寫入緩存的值,無(wú)疑是浪費(fèi)性能的。刪除緩存操作簡(jiǎn)單,副作用只是增加了一次cache miss,建議使用刪除策略。
2.2 先操作數(shù)據(jù)庫(kù)還是先操作緩存
2.2.1 先操作緩存
先操作緩存的流程如下:

先操作緩存的流程,就是先將緩存中數(shù)據(jù)刪除,再更新數(shù)據(jù)庫(kù)。
數(shù)據(jù)不一致
在讀寫并發(fā)操作的情況下,如何出現(xiàn)的數(shù)據(jù)不一致的問(wèn)題呢。先看下并發(fā)流程:

1. 線程1發(fā)起修改數(shù)據(jù)請(qǐng)求,會(huì)進(jìn)行刪除緩存操作。
2. 接著更新數(shù)據(jù)庫(kù)時(shí)出現(xiàn)了網(wǎng)絡(luò)延遲。
3. 線程1由于網(wǎng)絡(luò)延遲還未對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改,此時(shí)線程2執(zhí)行查詢請(qǐng)求,會(huì)去緩存中查詢數(shù)據(jù)。
4. 線程2在緩存中未查詢到數(shù)據(jù),再去查詢數(shù)據(jù)庫(kù)。
5. 線程2將查詢到數(shù)據(jù)舊數(shù)據(jù)放到緩存中,并將數(shù)據(jù)返回。
6. 線程1在線程2數(shù)據(jù)查詢完成后,才對(duì)數(shù)據(jù)庫(kù)進(jìn)行了修改。
在這個(gè)過(guò)程中就出現(xiàn)了redis與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致的問(wèn)題,只有等redis中數(shù)據(jù)過(guò)期時(shí)間到了,才能將新數(shù)據(jù)更新到緩存中。
2.2.2 先操作數(shù)據(jù)庫(kù)
先操作數(shù)據(jù)庫(kù)的流程如下:

先操作數(shù)據(jù)庫(kù)的流程,就是先對(duì)數(shù)據(jù)庫(kù)進(jìn)行修改,再將緩存中的數(shù)據(jù)進(jìn)行刪除。
數(shù)據(jù)不一致
在讀寫并發(fā)操作的情況下,如何出現(xiàn)的數(shù)據(jù)不一致的問(wèn)題呢。

1. 線程1發(fā)起修改數(shù)據(jù)請(qǐng)求,先更新數(shù)據(jù)庫(kù)。
2. 線程2在線程1更新數(shù)據(jù)庫(kù)期間,發(fā)起查詢請(qǐng)求,從緩存中獲取到舊數(shù)據(jù)(臟數(shù)據(jù))。
3. 線程1完成數(shù)據(jù)庫(kù)更新后,刪除緩存中的數(shù)據(jù)。
在這個(gè)過(guò)程中出現(xiàn)了短暫的數(shù)據(jù)不一致,但redis和數(shù)據(jù)庫(kù)數(shù)據(jù)是最終一致性的。所以推薦先操作數(shù)據(jù)庫(kù)再操作緩存。
通過(guò)上述原因分析,可以得出在并發(fā)的讀寫情況下,正常使用redis與數(shù)據(jù)庫(kù)不管是先操作redis還是先操作數(shù)據(jù)庫(kù),可能都會(huì)數(shù)據(jù)不一致問(wèn)題。
注意:由于redis和數(shù)據(jù)庫(kù)操作不是原子的,若在redis和數(shù)據(jù)庫(kù)之間加鎖是可以實(shí)現(xiàn)數(shù)據(jù)一致,但也違背了使用redis的初衷。
二、解決方案
在不考慮redis操作失敗的情況下,保證redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性的解決方案有4種。
1、延遲刪除機(jī)制
該機(jī)制是在數(shù)據(jù)庫(kù)數(shù)據(jù)更新后,先延遲一段時(shí)間后再次刪除緩存數(shù)據(jù)。線程1寫請(qǐng)求,線程2查詢請(qǐng)求,通過(guò)延遲雙刪機(jī)制保證redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性。

通過(guò)(6)步延遲一段時(shí)間后再進(jìn)行redis的刪除,在并發(fā)讀寫情況下保證redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性。具體延遲多長(zhǎng)時(shí)間,需評(píng)估項(xiàng)目讀數(shù)據(jù)業(yè)務(wù)邏輯耗時(shí)(即線程2從數(shù)據(jù)庫(kù)讀取數(shù)據(jù)到更新緩存成功的時(shí)間)。確保查詢請(qǐng)求結(jié)束,更新請(qǐng)求可以刪除查詢請(qǐng)求造成的緩存臟數(shù)據(jù)。
2、binlog同步刪除機(jī)制
通過(guò)canal組件訂對(duì)binlog日志進(jìn)行訂閱,模仿數(shù)據(jù)庫(kù)的slave數(shù)據(jù)庫(kù)的備份請(qǐng)求,使得redis緩存數(shù)據(jù)刪除,保證redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性。

通過(guò)上面兩種方式,在并發(fā)讀寫的情況下保證redis與數(shù)據(jù)庫(kù)數(shù)據(jù)最終一致性。但可能存在redis刪除失敗的情況,一旦出現(xiàn)就會(huì)有redis與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致的問(wèn)題。只有等redis中數(shù)據(jù)過(guò)期時(shí)間到了,才能將新數(shù)據(jù)更新到緩存中。
3、異步重試刪除機(jī)制
一旦緩存刪除失敗,可以通過(guò)重試機(jī)制設(shè)置重試次數(shù)保證一定刪除成功。如重試3次,三次操作都失敗則記錄日志并發(fā)送告警,通知技術(shù)人員進(jìn)行人工介入處理。在高并發(fā)環(huán)境下,重試最好使用異步方式,可以通過(guò)MQ實(shí)現(xiàn)這種機(jī)制。

通過(guò)(6)步延遲刪除緩存數(shù)據(jù)時(shí),刪除時(shí)失敗,緩存中存儲(chǔ)的還是臟數(shù)據(jù)(舊數(shù)據(jù))。線程1的應(yīng)用作為producer異步發(fā)送需要?jiǎng)h除key到MQ。線程1的應(yīng)用監(jiān)聽(tīng)MQ,重試刪除操作。
通過(guò)重試刪除機(jī)制,可以能夠保證redis緩存一定能刪除成功,保證redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性。但這種方式對(duì)業(yè)務(wù)代碼造成侵入,代碼過(guò)于耦合。
4、binlog解耦異步重試機(jī)制
可以使用阿里巴巴開源框架canal來(lái)實(shí)現(xiàn)程序解耦。通過(guò)利用canal提供的java客戶端,監(jiān)聽(tīng)canal通知消息。當(dāng)java客戶端(項(xiàng)目)收到binlog變化的消息時(shí),完成對(duì)緩存的處理。

數(shù)據(jù)庫(kù)更新后,canal訂閱binlog日志,將變更的數(shù)據(jù)發(fā)送消息通知給java客戶端(spring boot項(xiàng)目)。java客戶端執(zhí)行延遲刪除緩存,若刪除失敗,java客戶端作為producer異步發(fā)送需要?jiǎng)h除key的MQ消息進(jìn)行重試。客戶端監(jiān)聽(tīng)MQ消息,執(zhí)行重試刪除緩存操作。
三、總結(jié)
通過(guò)上述分析我們知道造成redis與數(shù)據(jù)庫(kù)數(shù)據(jù)不一致的問(wèn)題主要在于并發(fā)情況下,讀寫并發(fā)操作可能會(huì)出現(xiàn)這個(gè)問(wèn)題。通過(guò)4中解決方案,能夠很好的解決redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性問(wèn)題。
到此這篇關(guān)于Redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性的原因及解決方案的文章就介紹到這了,更多相關(guān)Redis與數(shù)據(jù)庫(kù)數(shù)據(jù)一致性解決內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- 詳解redis緩存與數(shù)據(jù)庫(kù)一致性問(wèn)題解決
- 面試常問(wèn):如何保證Redis緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性
- 淺談一下如何保證Redis緩存與數(shù)據(jù)庫(kù)的一致性
- MySQL數(shù)據(jù)庫(kù)和Redis緩存一致性的更新策略
- 如何保證Redis與數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性
- redis緩存與數(shù)據(jù)庫(kù)一致性的問(wèn)題及解決
- Redis緩存和數(shù)據(jù)庫(kù)的數(shù)據(jù)一致性的問(wèn)題解決
- Redis和數(shù)據(jù)庫(kù)的一致性(Canal+MQ) 的實(shí)現(xiàn)
相關(guān)文章
Redis高級(jí)數(shù)據(jù)類型Hyperloglog、Bitmap的使用
很多小伙伴在面試中都會(huì)被問(wèn)道 Redis的常用數(shù)據(jù)結(jié)構(gòu)有哪些?可能很大一部分回答都是 string、hash、list、set、zset,但其實(shí)還有Hyperloglog和Bitmap,本文就來(lái)介紹一下2021-05-05
redis配置standAlone版的jedisPool示例
這篇文章主要為大家介紹了redis配置standAlone版的jedisPool示例詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2023-07-07

