MySQL與Redis如何保證雙寫(xiě)一致性詳解
前言
首先,我們必須明確一個(gè)核心觀點(diǎn):在分布式環(huán)境下,要實(shí)現(xiàn)強(qiáng)一致性(在任何時(shí)刻讀取的數(shù)據(jù)都是最新的)是極其困難且代價(jià)高昂的,通常會(huì)嚴(yán)重犧牲性能。因此,在實(shí)踐中,我們通常追求最終一致性,即允許在短暫的時(shí)間內(nèi)數(shù)據(jù)不一致,但通過(guò)一些手段保證數(shù)據(jù)最終會(huì)保持一致。
下面我將從基礎(chǔ)概念、各種策略、最佳實(shí)踐到最新方案,為你詳細(xì)講解。
一、基礎(chǔ)概念:為什么會(huì)有不一致?
在一個(gè)包含 MySQL(作為可靠數(shù)據(jù)源)和 Redis(作為緩存)的系統(tǒng)中,所有的寫(xiě)操作(增、刪、改)都必須同時(shí)處理這兩個(gè)地方。

這個(gè)過(guò)程中,任何一步失敗或延遲都會(huì)導(dǎo)致不一致:
- 寫(xiě) MySQL 成功,寫(xiě) Redis 失敗:導(dǎo)致 Redis 中是舊數(shù)據(jù)。
- 寫(xiě) Redis 成功,寫(xiě) MySQL 失敗:導(dǎo)致 Redis 中是“臟數(shù)據(jù)”,數(shù)據(jù)庫(kù)中不存在。
- 并發(fā)讀寫(xiě):一個(gè)線(xiàn)程在更新數(shù)據(jù)庫(kù),但還沒(méi)更新緩存時(shí),另一個(gè)線(xiàn)程讀取了舊的緩存數(shù)據(jù)。
二、核心策略與模式
解決雙寫(xiě)一致性有多種策略,我們需要根據(jù)業(yè)務(wù)場(chǎng)景(對(duì)一致性的要求、讀寫(xiě)的比例等)進(jìn)行選擇。
策略一:Cache-Aside Pattern(旁路緩存模式)
這是最常用、最經(jīng)典的緩存模式。核心原則是:應(yīng)用程序直接與數(shù)據(jù)庫(kù)和緩存交互,緩存不作為寫(xiě)入的必經(jīng)之路。
- 讀流程:
- 收到讀請(qǐng)求。
- 首先查詢(xún) Redis,如果數(shù)據(jù)存在(緩存命中),直接返回。
- 如果 Redis 中沒(méi)有數(shù)據(jù)(緩存未命中),則從 MySQL 中查詢(xún)。
- 將從 MySQL 查詢(xún)到的數(shù)據(jù)寫(xiě)入 Redis(以便后續(xù)讀?。?,然后返回?cái)?shù)據(jù)。
- 寫(xiě)流程:
- 收到寫(xiě)請(qǐng)求。
- 更新 MySQL 中的數(shù)據(jù)。
- 刪除 Redis 中對(duì)應(yīng)的緩存。
為什么是刪除(Invalidate)緩存,而不是更新緩存?
這是一個(gè)關(guān)鍵設(shè)計(jì)點(diǎn)!
- 性能:如果更新緩存,每次數(shù)據(jù)庫(kù)寫(xiě)操作都要伴隨一次緩存寫(xiě)操作,如果該數(shù)據(jù)并不經(jīng)常被讀取,那么這次緩存寫(xiě)入就是浪費(fèi)資源的。
- 并發(fā)安全:在并發(fā)寫(xiě)場(chǎng)景下,更新緩存的順序可能與更新數(shù)據(jù)庫(kù)的順序不一致,導(dǎo)致緩存中是舊數(shù)據(jù)。而刪除操作是冪等的,更為安全。
Cache-Aside 如何保證一致性?
它通過(guò)“先更新數(shù)據(jù)庫(kù),再刪除緩存”來(lái)盡力保證。但它依然存在不一致的窗口期:
- 線(xiàn)程 A 更新數(shù)據(jù)庫(kù)。
- 線(xiàn)程 B 讀取數(shù)據(jù),發(fā)現(xiàn)緩存不存在,從數(shù)據(jù)庫(kù)讀取舊數(shù)據(jù)(因?yàn)?A 還沒(méi)提交或剛提交)。
- 線(xiàn)程 B 將舊數(shù)據(jù)寫(xiě)入緩存。
- 線(xiàn)程 A 刪除緩存。
這種情況發(fā)生的概率較低,因?yàn)橥ǔ?shù)據(jù)庫(kù)寫(xiě)操作(步驟1)會(huì)比讀操作(步驟2)耗時(shí)更長(zhǎng)(因?yàn)樯婕版i、日志等),所以步驟2在步驟1之前完成的概率很小。但這是一種理論上的可能。
策略二:Write-Through / Read-Through Pattern(穿透讀寫(xiě)模式)
在這種模式下,緩存層(或一個(gè)獨(dú)立的服務(wù))自己負(fù)責(zé)與數(shù)據(jù)庫(kù)交互。對(duì)應(yīng)用來(lái)說(shuō),它只與緩存交互。
- 寫(xiě)流程:應(yīng)用寫(xiě)入緩存,緩存組件同步地寫(xiě)入數(shù)據(jù)庫(kù)。只有兩個(gè)都成功后才會(huì)返回成功。
- 讀流程:應(yīng)用讀取緩存,如果未命中,緩存組件自己從數(shù)據(jù)庫(kù)加載并填充緩存,然后返回。
優(yōu)點(diǎn):邏輯對(duì)應(yīng)用透明,一致性比 Cache-Aside 更好。
缺點(diǎn):性能較差,因?yàn)槊看螌?xiě)操作都必然涉及一次數(shù)據(jù)庫(kù)寫(xiě)入。通常需要成熟的緩存中間件支持。
策略三:Write-Behind Pattern(異步寫(xiě)回模式)
Write-Through 的異步版本。應(yīng)用寫(xiě)入緩存后立即返回,緩存組件在之后某個(gè)時(shí)間點(diǎn)(例如攢夠一批數(shù)據(jù)或定時(shí))批量異步地更新到數(shù)據(jù)庫(kù)。
優(yōu)點(diǎn):寫(xiě)性能極高。
缺點(diǎn):有數(shù)據(jù)丟失風(fēng)險(xiǎn)(緩存宕機(jī)),一致性最弱。適用于允許少量數(shù)據(jù)丟失的場(chǎng)景,如計(jì)數(shù)、點(diǎn)贊等。
三、保證最終一致性的進(jìn)階方案
為了彌補(bǔ) Cache-Aside 模式中的缺陷,我們可以引入一些額外的機(jī)制。
方案一:延遲雙刪
針對(duì) Cache-Aside 中提到的“先更新數(shù)據(jù)庫(kù),再刪除緩存”可能帶來(lái)的并發(fā)問(wèn)題,可以引入一個(gè)延遲刪除。
- 線(xiàn)程 A 更新數(shù)據(jù)庫(kù)。
- 線(xiàn)程 A 刪除緩存。
- 線(xiàn)程 A 休眠一個(gè)特定的時(shí)間(如 500ms - 1s)。
- 線(xiàn)程 A 再次刪除緩存。
第二次刪除是為了清理掉在第1次刪除后、其他線(xiàn)程可能寫(xiě)入的舊數(shù)據(jù)。這個(gè)休眠時(shí)間需要根據(jù)業(yè)務(wù)讀寫(xiě)耗時(shí)來(lái)估算。
優(yōu)點(diǎn):簡(jiǎn)單有效,能很大程度上解決并發(fā)讀寫(xiě)導(dǎo)致的不一致。
缺點(diǎn):降低了寫(xiě)入吞吐量,休眠時(shí)間難以精確設(shè)定。
方案二:通過(guò)消息隊(duì)列異步刪除
為了解耦和重試,可以將刪除緩存的操作作為消息發(fā)送到消息隊(duì)列(如 RocketMQ, Kafka)。
- 更新數(shù)據(jù)庫(kù)。
- 向消息隊(duì)列發(fā)送一條刪除緩存的消息。
- 消費(fèi)者消費(fèi)該消息,執(zhí)行刪除 Redis 的操作。如果刪除失敗,消息會(huì)重試。
這保證了刪除緩存的操作至少會(huì)被執(zhí)行一次,大大提高了可靠性。
方案三:通過(guò)數(shù)據(jù)庫(kù) Binlog 同步(最優(yōu)解)
這是目前最成熟、對(duì)業(yè)務(wù)侵入性最小、一致性最好的方案。其核心是利用 MySQL 的二進(jìn)制日志(Binlog)進(jìn)行增量數(shù)據(jù)同步。
工作原理:
- 業(yè)務(wù)系統(tǒng)正常寫(xiě)入 MySQL。
- 由一個(gè)中間件(如 Canal, Debezium)偽裝成 MySQL 的從庫(kù),訂閱 Binlog。
- 中間件解析 Binlog,獲取數(shù)據(jù)的變更詳情(增、刪、改)。
- 中間件根據(jù)變更,調(diào)用 Redis 的 API 來(lái)更新或刪除對(duì)應(yīng)的緩存。

優(yōu)點(diǎn):
- 業(yè)務(wù)無(wú)侵入:業(yè)務(wù)代碼只關(guān)心寫(xiě)數(shù)據(jù)庫(kù),完全不知道緩存的存在。
- 高性能:數(shù)據(jù)庫(kù)和緩存的同步是異步的,不影響主業(yè)務(wù)鏈路的性能。
- 強(qiáng)保證:由于基于 Binlog,它能保證只要數(shù)據(jù)庫(kù)變了,緩存最終一定會(huì)被同步。順序也與數(shù)據(jù)庫(kù)一致。
缺點(diǎn):
- 架構(gòu)復(fù)雜,需要維護(hù)額外的同步組件。
- 同步有毫秒級(jí)到秒級(jí)的延遲。
四、總結(jié)與最佳實(shí)踐選擇
策略 | 一致性保證 | 性能 | 復(fù)雜度 | 適用場(chǎng)景 |
Cache-Aside + 刪除 | 最終一致性(有微弱不一致風(fēng)險(xiǎn)) | 高 | 低 | 絕大多數(shù)場(chǎng)景的首選,讀多寫(xiě)少 |
Cache-Aside + 延遲雙刪 | 更好的最終一致性 | 中 | 低 | 對(duì)一致性要求稍高,且能接受一定延遲的寫(xiě)操作 |
Write-Through | 強(qiáng)一致性 | 中 | 中 | 寫(xiě)多讀少,且對(duì)一致性要求非常高的場(chǎng)景 |
Binlog 同步 | 最終一致性(推薦) | 高 | 高 | 大型、高要求項(xiàng)目的最佳實(shí)踐,對(duì)業(yè)務(wù)無(wú)侵入 |
通用建議:
- 首選方案:對(duì)于大多數(shù)應(yīng)用,從 Cache-Aside(先更新數(shù)據(jù)庫(kù),再刪除緩存) 開(kāi)始。它簡(jiǎn)單、有效,在大多數(shù)情況下已經(jīng)足夠。
- 進(jìn)階保障:如果 Cache-Aside 的不一致窗口無(wú)法接受,可以引入延遲雙刪或消息隊(duì)列異步刪除來(lái)增強(qiáng)。
- 終極方案:當(dāng)業(yè)務(wù)發(fā)展到一定規(guī)模,對(duì)一致性和系統(tǒng)解耦有更高要求時(shí),投入資源搭建基于 Binlog 的異步同步方案。這是業(yè)界證明最可靠的方案。
- 設(shè)置合理的過(guò)期時(shí)間:無(wú)論如何,都給 Redis 中的緩存設(shè)置一個(gè)過(guò)期時(shí)間(TTL)。這是一個(gè)安全網(wǎng),即使同步邏輯出現(xiàn)問(wèn)題,舊數(shù)據(jù)也會(huì)自動(dòng)失效,最終從數(shù)據(jù)庫(kù)加載新數(shù)據(jù),保證最終一致性。
- 業(yè)務(wù)容忍度:最重要的是,與產(chǎn)品經(jīng)理確認(rèn)業(yè)務(wù)對(duì)一致性的容忍度。很多時(shí)候,1-2秒內(nèi)的數(shù)據(jù)不一致用戶(hù)是感知不到的,不需要為此付出巨大的架構(gòu)和性能代價(jià)。
總結(jié)
到此這篇關(guān)于MySQL與Redis如何保證雙寫(xiě)一致性的文章就介紹到這了,更多相關(guān)MySQL與Redis保證雙寫(xiě)一致性?xún)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
MySQL執(zhí)行過(guò)程中選擇最佳的執(zhí)行路徑的方法(核心流程)
文章介紹MySQL查詢(xún)優(yōu)化器如何通過(guò)解析、生成執(zhí)行計(jì)劃及成本評(píng)估選擇最佳路徑,依賴(lài)統(tǒng)計(jì)信息如表行數(shù)和索引基數(shù),開(kāi)發(fā)者需創(chuàng)建索引、更新統(tǒng)計(jì)信息、優(yōu)化SQL寫(xiě)法及使用EXPLAIN工具輔助優(yōu)化,感興趣的朋友跟隨小編一起看看吧2025-09-09
一文帶你了解MySQL之InnoDB統(tǒng)計(jì)數(shù)據(jù)是如何收集的
通過(guò)show index可以看到關(guān)于索引的統(tǒng)計(jì)數(shù)據(jù),那么這些統(tǒng)計(jì)數(shù)據(jù)是怎么來(lái)的呢,它們是以什么方式收集的呢,本章將聚焦于InnoDB存儲(chǔ)引擎的統(tǒng)計(jì)數(shù)據(jù)收集策略,需要的朋友可以參考下2023-05-05
Windows7下安裝使用MySQL8.0.16修改密碼、連接Navicat問(wèn)題
這篇文章主要介紹了Windows7下安裝使用MySQL8.0.16修改密碼、連接Navicat問(wèn)題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06
MySQL在右表數(shù)據(jù)不唯一的情況下使用left join的方法
這篇文章主要介紹了MySQL在右表數(shù)據(jù)不唯一的情況下使用left join的方法,針對(duì)右表符合條件表達(dá)式的記錄數(shù)大于1條時(shí)left join所顯示的結(jié)果需求來(lái)講,需要的朋友可以參考下2016-03-03
解決當(dāng)MySQL數(shù)據(jù)庫(kù)遇到Syn Flooding問(wèn)題
Syn攻擊常見(jiàn)于應(yīng)用服務(wù)器,而數(shù)據(jù)庫(kù)服務(wù)器在內(nèi)網(wǎng)中,應(yīng)該很難碰到類(lèi)似的攻擊,這篇文章主要介紹了當(dāng)MySQL數(shù)據(jù)庫(kù)遇到Syn Flooding問(wèn)題 ,需要的朋友可以參考下2019-06-06

