如何實現Redis和Mysql中數據雙寫一致性

一、引言

今天我們來聊聊一個在分布式系統中非常常見但又十分棘手的問題——Redis與MySQL之間的雙寫一致性。我們在項目中多多少少都遇到過類似的困擾,緩存是用Redis,數據庫是用MySQL,但如何確保兩者之間的數據一致性呢?接下來我會盡量簡潔地為大家解析這個問題,并提供幾個實戰方案。

二、雙寫一致性挑戰

我們先來看看什么是雙寫一致性。

簡單來說,就是當數據同時存在于緩存(Redis)和數據庫(MySQL)時,如何確保這兩者之間的數據是一致的。

典型場景

  1. 寫數據庫后忘記更新緩存:這種情況最常見,當我們更新數據庫后,緩存沒有同步更新,導致讀取到舊的數據。
  2. 刪除緩存后數據庫更新失敗:在某些操作中,我們可能會先刪除緩存,再更新數據庫,但如果數據庫更新失敗,就會導致緩存和數據庫的數據不一致。

在了解完雙寫一致性帶來的挑戰之后我們接下來看看幾種經典的緩存模式。

三、緩存模式

3.1?Cache Aside Pattern (旁路模式)

Cache Aside Pattern是最常見的一種緩存使用模式,它的核心思想是以數據庫為主,緩存為輔。

工作流程

讀取操作:先從緩存中讀取數據,如果緩存命中則返回結果;如果緩存未命中,則從數據庫中讀取數據,并將數據寫入緩存。

更新操作:先更新數據庫,再刪除緩存中的舊數據。

示例代碼:

public class CacheAsidePattern {private RedisService redis;private DatabaseService database;// 讀取操作public String getData(String key) {// 從緩存中獲取數據String value = redis.get(key);if (value == null) {// 緩存未命中,從數據庫獲取數據value = database.get(key);if (value != null) {// 將數據寫入緩存redis.set(key, value);}}return value;}// 更新操作public void updateData(String key, String value) {// 更新數據庫database.update(key, value);// 刪除緩存中的舊數據redis.delete(key);}
}

優缺點分析:

優點:

  • 簡單易懂,易于實現。
  • 讀性能高,因為大部分讀操作都會命中緩存。

缺點:

  • 存在短暫的不一致情況,更新數據庫后緩存可能還沒刪除。
  • 刪除緩存后,如果數據庫更新失敗,會導致數據不一致。

3.2?讀寫穿透模式

3.2.1 寫穿透

當緩存未命中時,自動從數據庫加載數據,并寫入緩存。

3.2.2 讀穿透

當緩存更新時,同步將數據寫入數據庫。

3.2.3 代碼示例
public class ReadWriteThroughPattern {private RedisService redis;private DatabaseService database;// Read-Throughpublic String readThrough(String key) {// 從緩存中獲取數據String value = redis.get(key);if (value == null) {// 緩存未命中,從數據庫獲取數據value = database.get(key);if (value != null) {// 將數據寫入緩存redis.set(key, value);}}return value;}// Write-Throughpublic void writeThrough(String key, String value) {// 將數據寫入緩存redis.set(key, value);// 同步將數據寫入數據庫database.update(key, value);}
}
3.2.4?優缺點分析

優點:

  • 保證了數據的強一致性,緩存和數據庫的數據始終同步。
  • 讀寫操作都由緩存處理,數據庫壓力較小。

缺點:

  • 寫操作的延遲較高,因為每次寫入緩存時都需要同步寫入數據庫。
  • 實現復雜度較高,需要額外的緩存同步機制。

3.3?異步緩存寫入(Write Behind)

緩存更新后,異步批量寫入數據庫。這種策略適用于可以容忍一定數據不一致的高性能場景。

3.3.1 示例代碼
public class WriteBehindPattern {private RedisService redis;private DatabaseService database;private UpdateQueue updateQueue;// 異步緩存寫入public void writeBehind(String key, String value) {// 將數據寫入緩存redis.set(key, value);// 異步將數據寫入數據庫asyncDatabaseUpdate(key, value);}private void asyncDatabaseUpdate(String key, String value) {// 異步操作,將更新請求放入隊列updateQueue.add(new UpdateTask(key, value));}
}
3.3.2?優缺點分析

優點:

  • 寫操作的性能非常高,因為只需更新緩存,數據庫更新是異步進行的。
  • 適用于對寫操作性能要求較高的場景。

缺點:

  • 存在數據不一致的風險,緩存更新后數據庫可能還未更新。
  • 實現復雜度較高,需要處理異步操作中的異常和重試。

四、實戰解析

4.1?延時雙刪策略

延時雙刪策略的核心思想是:在更新數據庫后,先刪除一次緩存,然后延遲一段時間再刪除一次緩存,減少數據不一致的風險。

關鍵是如何確定延遲時間,這個時間需要根據系統的具體情況來調整,以平衡一致性和性能。

public class DelayedDoubleDeletePattern {private RedisService redis;private DatabaseService database;private ScheduledExecutorService scheduledExecutorService;private long delay = 500; // 延遲時間,單位:毫秒// 更新操作public void updateDataWithDelay(String key, String value) {// 更新數據庫database.update(key, value);// 刪除緩存中的舊數據redis.delete(key);// 延遲一段時間再刪除緩存scheduledExecutorService.schedule(() -> redis.delete(key), delay, TimeUnit.MILLISECONDS);}
}

優缺點分析:

優點:

  • 簡化了緩存和數據庫的一致性問題。
  • 避免了緩存和數據庫的同步更新,提高了系統性能。

缺點:

  • 需要精確控制延遲時間,否則可能導致緩存和數據庫不一致。
  • 實現相對復雜,需要額外的定時任務管理。

4.2?刪除緩存重試機制

刪除緩存時,如果失敗,可以設置重試機制,以確保緩存最終被刪除。

通過使用Spring的@Retryable注解,可以簡化重試邏輯。

代碼示例:

public class CacheService {private RedisService redis;@Retryable(value = Exception.class, maxAttempts = 5, backoff = @Backoff(delay = 2000))public void deleteCache(String key) {// 刪除緩存中的數據redis.delete(key);}
}

優缺點分析:

優點:

  • 確保緩存最終被刪除,降低數據不一致的風險。
  • 使用Spring的重試機制,簡化實現邏輯。

缺點:

  • 需要處理重試的多次失敗情況,可能導致系統負載增加。
  • 適用于緩存刪除失敗率較低的場景。

4.3?監聽binlog異步刪除緩存

利用數據庫的binlog變更來異步更新緩存,通過消息隊列和異步服務解耦緩存更新操作。

通過訂閱binlog,將變更記錄放入消息隊列,然后由異步服務處理緩存更新。

代碼示例:

public class BinlogListenerPattern {private RedisService redis;private MessageQueue messageQueue;// 訂閱binlogpublic void onBinlogChange(BinlogEntry entry) {// 將變更記錄放入消息隊列messageQueue.send(new CacheUpdateMessage(entry.getKey()));}// 異步服務處理緩存更新public void processCacheUpdate(CacheUpdateMessage message) {// 刪除緩存中的數據redis.delete(message.getKey());}
}

優缺點分析:

優點:

  • 利用數據庫的變更日志,保證緩存和數據庫的一致性。
  • 異步處理提高了系統性能,降低了實時更新的壓力。

缺點:

  • 實現復雜度較高,需要處理消息隊列和異步服務。
  • 存在延遲更新的情況,可能導致短時間內的數據不一致。

五、小結

在實際項目中,我們需要根據具體的業務場景來選擇最合適的一致性策略。同時,在高并發場景下,可以結合分布式鎖和消息隊列來確保數據一致性。異步處理中的異常處理和重試策略也非常重要,能夠有效提高系統的穩定性和可靠性。

此外,在實際開發應用時,不需要自己再去實現一套緩存管理代碼,有很多框架已經提供了基于聲明式注解的緩存管理器抽象,只需要添加幾個注解,就可以實現數據庫緩存,例如:

  • Spring Cache
  • Alibaba Jetcache

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/81039.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/81039.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/81039.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

面試篇 - Transformer前饋神經網絡(FFN)使用什么激活函數?

1. FFN結構分解 原始Transformer的FFN層 FFN(x) max(0, xW? b?)W? b? # 原始論文公式 輸入:自注意力層的輸出 x(維度 d_model512) 擴展層:xW? b?(擴展為 d_ff2048) 激活函數:Re…

基于Python Flask的深度學習電影評論情感分析可視化系統(2.0升級版,附源碼)

博主介紹:?IT徐師兄、7年大廠程序員經歷。全網粉絲15W、csdn博客專家、掘金/華為云//InfoQ等平臺優質作者、專注于Java技術領域和畢業項目實戰? 🍅文末獲取源碼聯系🍅 👇🏻 精彩專欄推薦訂閱👇&#x1f3…

前端vue2修改echarts字體為思源黑體-避免侵權-可以更換為任意字體統一管理

1.下載字體 npm install fontsource/noto-sans-sc 不知道為什么我從github上面下載的不好使,所以就用了npm的 2.引用字體 import fontsource/noto-sans-sc; 在入口文件-main.js中引用 3.設置echats模板樣式 import * as echarts from echarts; // 在import的后…

51c自動駕駛~合集37

我自己的原文哦~ https://blog.51cto.com/whaosoft/13878933 #DETR->DETR3D->Sparse4D 走向長時序稀疏3D目標檢測 一、DETR 圖1 DETR架構 DETR是第一篇將Transformer應用到目標檢測方向的算法。DETR是一個經典的Encoder-Decoder結構的算法,它的骨干網…

【MongoDB篇】MongoDB的集合操作!

目錄 引言第一節:集合的“誕生”——自動出現還是手動打造?🤔第二節:集合的“查閱”——看看這個數據庫里有哪些柜子?📂👀第三節:集合的“重命名”——給文件柜換個名字!…

Goland終端PowerShell命令失效

Goland終端Terminal的PowerShell不能使用,明明windows上升級了PowerShell 7設置了配置文件,但是只能在windows終端下使用,goland終端下直接失效報錯,安裝升級PowerShell請看Windows11終端升級PowerShell7 - HashFlag - 博客園 問…

簡單分析自動駕駛發展現狀與挑戰

一、技術進展與市場滲透 技術分級與滲透率 當前量產乘用車的自動駕駛等級以L2為主(滲透率約51%),L3級處于初步落地階段(滲透率約20%),而L4級仍處于測試和示范運營階段(滲透率約11%)2…

【C++類和數據抽象】消息處理示例(1):從設計模式到實戰應用

目錄 一、數據抽象概述 二、消息處理的核心概念 2.1 什么是消息處理? 2.2 消息處理的核心目標 三、基于設計模式的消息處理實現 3.1 觀察者模式(Observer Pattern) 3.2 命令模式(Command Pattern) 四、實戰場景…

【統計方法】交叉驗證:Resampling, nested 交叉驗證等策略 【含R語言】

Resampling (重采樣方法) 重采樣方法是從訓練數據中反復抽取樣本,并在每個(重新)樣本上重新調整模型,以獲得關于擬合模型的附加信息的技術。 兩種主要的重采樣方法 Cross-Validation (CV) 交叉驗證 : 用于估計測試誤…

常見的 CSS 知識點整理

1. 盒模型(Box Model)是什么?標準盒模型和 IE 盒模型的區別? 答案: CSS 盒模型將元素視為一個盒子,由內容(content)、內邊距(padding)、邊框(bor…

Educational Codeforces Round 178 div2(題解ABCDE)

A. Three Decks #1.由于最后三個數會相等&#xff0c;提前算出來和&#xff0c;%3判斷&#xff0c;再判前兩個數是否大于 #include<iostream> #include<vector> #include<stdio.h> #include<map> #include<string> #include<algorithm> #…

如何創建一個導入模板?全流程圖文解析

先去找到系統內可以上傳東西的按鈕 把你的模板上傳上去,找到對應的fileName 圖里的文字寫錯了,是復制粘貼"filePath"到URL才能下載

通信原理第七版與第六版區別附pdf

介紹 我用夸克網盤分享了「通信原理 第7版》樊昌信」&#xff0c;鏈接&#xff1a;https://pan.quark.cn/s/be7c5af4cdce 《通信原理&#xff08;第7版&#xff09;》是在第6版的基礎上&#xff0c;為了適應當前通信技術發展和教學需求&#xff0c;并吸取了數十所院校教師的反…

Mysql唯一性約束

唯一性約束&#xff08;Unique Constraint&#xff09;是數據庫設計中用于保證表中某一列或多列組合的值具有唯一性的一種規則。它可以防止在指定列中插入重復的數據&#xff0c;有助于維護數據的完整性和準確性。下面從幾個方面為你詳細解釋 作用 確保數據準確性&#xff1a…

測試基礎筆記第十六天

提示&#xff1a;文章寫完后&#xff0c;目錄可以自動生成&#xff0c;如何生成可參考右邊的幫助文檔 文章目錄 一、UI自動化介紹1.認識UI自動化測試2.實施UI自動化測試前置條件3.UI自動化測試執行時機4.UI自動化測試核心作用和劣勢 二、認識Web自動化測試工具-Selenium021.Sel…

PaddleX的安裝

參考&#xff1a;安裝PaddlePaddle - PaddleX 文檔 1、安裝PaddlePaddle 查看 docker 版本 docker --version 若您通過 Docker 安裝&#xff0c;請參考下述命令&#xff0c;使用飛槳框架官方 Docker 鏡像&#xff0c;創建一個名為 paddlex 的容器&#xff0c;并將當前工作目…

長效住宅IP是什么?如何獲取長效住宅IP?

在當今的互聯網世界里&#xff0c;IP地址作為連接用戶與網站之間的橋梁&#xff0c;其重要性不言而喻。對于跨境電商、社交媒體運營以及數據采集等領域的專業人士而言&#xff0c;普通的IP地址已無法滿足日益復雜的需求。他們更需要一種穩定、安全且持久的長效住宅IP來完成各類…

02 業務流程架構

業務流程架構提供了自上而下的組織鳥瞰圖&#xff0c;是業務流程的全景圖。根據所采用的方法不同&#xff0c;有時被稱為流程全景圖或高層級流程圖&#xff0c;提供了業務運營中所有業務流程的整體視圖。 這樣有助于理解企業內部各個業務流程之間的相互關系以及它們如何共同工…

jenkins slave節點打包報錯Failed to create a temp file on

jenkins slave節點打包報錯 一、報錯信息 FATAL: Unable to produce a script file Also: hudson.remoting.Channel$CallSiteStackTrace: Remote call to slave-83at hudson.remoting.Channel.attachCallSiteStackTrace(Channel.java:1784)at hudson.remoting.UserRequest$…

什么是 Swagger 以及如何在 Spring Boot 中實現 Swagger:配置與實踐指南

在現代 RESTful API 開發中&#xff0c;Swagger 是一種廣泛使用的工具&#xff0c;用于生成、描述和可視化 API 文檔。它極大地簡化了 API 的開發、測試和維護過程。結合 Spring Boot&#xff0c;Swagger 可以快速集成到項目中&#xff0c;生成交互式 API 文檔&#xff0c;方便…