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

1. 應用程序需要從數據庫讀取數據時,先查詢redis的緩存數據是否命中。
2. 若命中,直接返回。若未命中,再去查詢數據庫。
3. 將查詢到的數據先保存到redis中,并設置過期時間,再將數據返回到應用。
以上是常用的一個讀取數據的場景,根據場景分析,只讀的情況下是不會出現(xiàn)redis與數據庫數據不一致的情況。
2、寫數據
寫數據流程一般操作流程可以分為以下4種:
1. 先更新緩存再更新數據庫。
2. 先刪除緩存再更新數據庫。
3. 先更新數據庫再更新緩存。
4. 先更新數據庫再刪除緩存。
根據以上4種流程分析,可以明確出兩個問題,一個是緩存是更新還是刪除,另外一個是先操作數據庫還是先操作緩存呢。
2.1、緩存是更新還是刪除
推薦使用刪除緩存。因為緩存的更新成本太高。由于大多數情況下數據并不是直接寫入緩存的,需要經過一系列復雜的計算再寫入緩存的。若采用更新方式,那么每次寫入數據庫后,都需計算寫入緩存的值,無疑是浪費性能的。刪除緩存操作簡單,副作用只是增加了一次cache miss,建議使用刪除策略。
2.2 先操作數據庫還是先操作緩存
2.2.1 先操作緩存
先操作緩存的流程如下:

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

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

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

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

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

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

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

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

