如何保障MySQL和Redis的數據一致性?

来源:https://www.cnblogs.com/88223100/archive/2023/10/21/How-to-ensure-data-consistency-between-MySQL-and-Redis.html
-Advertisement-
Play Games

先拋一下結論:在滿足實時性的條件下,不存在兩者完全保存一致的方案,只有最終一致性方案。根據網上的眾多解決方案,總結出 6 種,直接看目錄: 不好的方案 1、先寫 MySQL,再寫 Redis 如圖所示: 這是一副時序圖,描述請求的先後調用順序; 橘黃色的線是請求 A,黑色的線是請求 B; 橘黃色的文 ...


先拋一下結論:在滿足實時性的條件下,不存在兩者完全保存一致的方案,只有最終一致性方案。根據網上的眾多解決方案,總結出 6 種,直接看目錄:

 

圖片

 

不好的方案

 

1、先寫 MySQL,再寫 Redis

 

圖片

 

 

如圖所示:

 

  • 這是一副時序圖,描述請求的先後調用順序;

 

  • 橘黃色的線是請求 A,黑色的線是請求 B;

 

  • 橘黃色的文字,是 MySQL 和 Redis 最終不一致的數據;

 

  • 數據是從 10 更新為 11;

 

  • 後面所有的圖,都是這個含義,不再贅述。

 

請求 A、B 都是先寫 MySQL,然後再寫 Redis,在高併發情況下,如果請求 A 在寫 Redis 時卡了一會,請求 B 已經依次完成數據的更新,就會出現圖中的問題。

 

這個圖已經畫得很清晰了,我就不用再去啰嗦了吧,不過這裡有個前提,就是對於讀請求,先去讀 Redis,如果沒有,再去讀 DB,但是讀請求不會再回寫 Redis。大白話說一下,就是讀請求不會更新 Redis。

 

2、先寫 Redis,再寫 MySQL

 

圖片

 

 

同“先寫 MySQL,再寫 Redis”,看圖可秒懂。

 

3、先刪除 Redis,再寫 MySQL

 

這幅圖和上面有些不一樣,前面的請求 A 和 B 都是更新請求,這裡的請求 A 是更新請求,但是請求 B 是讀請求,且請求 B 的讀請求會回寫 Redis。

 

圖片

 

 

請求 A 先刪除緩存,可能因為卡頓,數據一直沒有更新到 MySQL,導致兩者數據不一致。

 

這種情況出現的概率比較大,因為請求 A 更新 MySQL 可能耗時會比較長,而請求 B 的前兩步都是查詢,會非常快。

 

好的方案

 

1、先刪除 Redis,再寫 MySQL,再刪除 Redis

 

對於“先刪除 Redis,再寫 MySQL”,如果要解決最後的不一致問題,其實再對 Redis 重新刪除即可,這個也是大家常說的“緩存雙刪”。

 

圖片

 

 

為了便於大家看圖,對於藍色的文字,“刪除緩存 10”必須在“回寫緩存10”後面,那如何才能保證一定是在後面呢?網上給出的第一個方案是,讓請求 A 的最後一次刪除,等待 500ms。

 

對於這種方案,看看就行,反正我是不會用,太 Low 了,風險也不可控。

 

那有沒有更好的方案?我建議非同步串列化刪除,即刪除請求入隊列。

 

圖片

 

 

非同步刪除對線上業務無影響,串列化處理保障併發情況下正確刪除。

 

如果雙刪失敗怎麼辦,網上有給 Redis 加一個緩存過期時間的方案,這個不敢苟同。個人建議整個重試機制,可以藉助消息隊列的重試機制,也可以自己整個表,記錄重試次數,方法很多。

 

簡單小結一下:

 

  • “緩存雙刪”不要用無腦的 sleep 500 ms;

 

  • 通過消息隊列的非同步&串列,實現最後一次緩存刪除;

 

  • 緩存刪除失敗,增加重試機制。

 

2、先寫 MySQL,再刪除 Redis

 

圖片

 

 

對於上面這種情況,對於第一次查詢,請求 B 查詢的數據是 10,但是 MySQL 的數據是 11,只存在這一次不一致的情況,對於不是強一致性要求的業務,可以容忍。(什麼情況下不能容忍呢,比如秒殺業務、庫存服務等。)

 

當請求 B 進行第二次查詢時,因為沒有命中 Redis,會重新查一次 DB,然後再回寫到 Reids。

 

圖片

 

 

這裡需要滿足 2 個條件:

 

  • 緩存剛好自動失效;

  • 請求 B 從資料庫查出 10,回寫緩存的耗時,比請求 A 寫資料庫,並且刪除緩存的還長。

 

對於第二個條件,我們都知道更新 DB 肯定比查詢耗時要長,所以出現這個情況的概率很小,同時滿足上述條件的情況更小。

 

3、先寫 MySQL,通過 Binlog,非同步更新 Redis

 

這種方案,主要是監聽 MySQL 的 Binlog,然後通過非同步的方式,將數據更新到 Redis,這種方案有個前提,查詢的請求,不會回寫 Redis。

 

圖片

 

 

這個方案,會保證 MySQL 和 Redis 的最終一致性,但是如果中途請求 B 需要查詢數據,如果緩存無數據,就直接查 DB;如果緩存有數據,查詢的數據也會存在不一致的情況。

 

所以這個方案,是實現最終一致性的終極解決方案,但是不能保證實時性。

 

幾種方案比較

 

我們對比上面討論的 6 種方案:

 

1、先寫 Redis,再寫 MySQL

 

這種方案,我肯定不會用,萬一 DB 掛了,你把數據寫到緩存,DB 無數據,這個是災難性的。

 

我之前也見同學這麼用過,如果寫 DB 失敗,對 Redis 進行逆操作,那如果逆操作失敗呢,是不是還要搞個重試?

 

2、先寫 MySQL,再寫 Redis

 

對於併發量、一致性要求不高的項目,很多就是這麼用的,我之前也經常這麼搞,但是不建議這麼做。

 

當 Redis 瞬間不可用的情況,需要報警出來,然後線下處理。

 

3、先刪除 Redis,再寫 MySQL

 

這種方式,我還沒使用過,直接忽略吧。

 

4、先刪除 Redis,再寫 MySQL,再刪除 Redis

 

這種方式雖然可行,但是感覺好複雜,還要搞個消息隊列去非同步刪除 Redis。

 

5、先寫 MySQL,再刪除 Redis

 

比較推薦這種方式,刪除 Redis 如果失敗,可以再多重試幾次,否則報警出來;這個方案,是實時性中最好的方案,在一些高併發場景中,推薦這種。

 

6、先寫 MySQL,通過 Binlog,非同步更新 Redis

 

對於異地容災、數據彙總等,建議會用這種方式,比如 binlog + kafka,數據的一致性也可以達到秒級。

 

純粹的高併發場景,不建議用這種方案,比如搶購、秒殺等。

 

結論

 

  • 實時一致性方案:採用“先寫 MySQL,再刪除 Redis”的策略,這種情況雖然也會存在兩者不一致,但是需要滿足的條件有點苛刻,所以是滿足實時性條件下,能儘量滿足一致性的最優解。

 

  • 最終一致性方案:採用“先寫 MySQL,通過 Binlog,非同步更新 Redis”,可以通過 Binlog,結合消息隊列非同步更新 Redis,是最終一致性的最優解。

 

作者丨樓仔

本文來自博客園,作者:古道輕風,轉載請註明原文鏈接:https://www.cnblogs.com/88223100/p/How-to-ensure-data-consistency-between-MySQL-and-Redis.html


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 簡介 Go語言中的切片(slice)是一種靈活的數據結構,它構建在數組之上並提供了方便的方式來操作數組的一部分。切片的底層實現涉及到數組和一些元數據。以下是Golang切片的底層實現的詳細介紹: 底層數組(Underlying Array): 切片是建立在一個底層數組之上的。這個數組通常比切片的容量 ...
  • 1 概覽 DataX 是一個異構數據源離線同步工具,致力於實現包括關係型資料庫(MySQL、Oracle等)、HDFS、Hive、ODPS、HBase、FTP等各種異構數據源之間穩定高效的數據同步功能。 1.1 設計理念 為瞭解決異構數據源同步問題,DataX將複雜的網狀的同步鏈路變成了星型數據鏈路 ...
  • 我們應對併發場景時一般會採用下麵方式去預估線程池的線程數量,但是在一些情況下,這個t是不好估算的,即便是估算出來了,在實際的線程環境上也需要進行驗證和微調。比如在本文所闡述分頁查詢的數據項組合場景中。 ...
  • 氣泡圖是一種多變數的統計圖表,可以看作是散點圖的變形。與散點圖不同的是,每一個氣泡都表示三個維度的數據,除了像散點圖一樣有X,Y軸,氣泡的大小可以表示另一個維度的數據。例如,x軸表示產品銷量,y軸表示產品利潤,氣泡大小代表產品市場份額百分比。 它可以幫助我們發現變數之間的模式、趨勢和異常值。通過氣泡 ...
  • 埠掃描是一種網路安全測試技術,該技術可用於確定對端主機中開放的服務,從而在滲透中實現信息搜集,其主要原理是通過發送一系列的網路請求來探測特定主機上開放的`TCP/IP`埠。具體來說,埠掃描程式將從指定的起始埠開始,向目標主機發送一條`TCP`或`UDP`消息(這取決於埠的協議類型)。如果目... ...
  • 對比yaml,toml,json三種格式的優缺點及三種格式的支持特性,以及講述了nginx.conf轉化成yaml,toml格式的樣式 ...
  • 支持.Net Core(2.0及以上)與.Net Framework(4.5及以上) 本文所述方案近期被江蘇省某億級數據量+高併發的政府"物聯網"項目採用,獲得圓滿成功!! 【目錄】 發送消息、獲取消息、使用消息 延時隊列 & 死信隊列 展望 RabbitMQ作為一款主流的消息隊列工具早已廣受歡迎。 ...
  • 1. on-my-zsh安裝 1.1. 使用curl方式安裝 1.1.1 官方鏡像源 sh -c "$(curl -fsSL https://raw.githubusercontent.com/ohmyzsh/ohmyzsh/master/tools/install.sh)" 1.1.2 國內Git ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...