Redis與MySQL的雙寫一致性問題
Redis與MySQL雙寫一致性是指在使用緩存和數(shù)據(jù)庫同時存儲數(shù)據(jù)的場景下( 主要是存在高并發(fā)的情況),如何保證兩者的數(shù)據(jù)一致性(內(nèi)容相同或者盡可能接近)。
正常業(yè)務(wù)流程:
讀沒什么問題,關(guān)鍵就在于寫(更新)操作,這就會出現(xiàn)幾個問題了,這里是先更新數(shù)據(jù)庫,然后對緩存操作。但對于緩存操作,是更新緩存還是刪除緩存呢?或者為什么不是先操作(刪除、更新)緩存在更新數(shù)據(jù)庫呢?
總結(jié)一下就是到底先操作緩存再操作數(shù)據(jù)庫,還是先操作數(shù)據(jù)庫再操作緩存?
帶著這幾個問題接著往下講。
首先講一下操作緩存,包括兩種:更新緩存和刪除緩存,如何選擇?
更新緩存? 刪除緩存?
假設(shè)都先更新數(shù)據(jù)庫(因為先操作緩存再操作數(shù)據(jù)庫問題較大,后面會講)
- 更新緩存
先更新數(shù)據(jù)庫,再更新緩存。
如果兩個請求同時對同一條數(shù)據(jù)進(jìn)行修改,那么可能出現(xiàn)先后順序顛倒,導(dǎo)致緩存中的數(shù)據(jù)是舊的。之后的讀請求讀到的都是舊數(shù)據(jù),只有當(dāng)緩存失效后,才能從數(shù)據(jù)庫中得到正確的值。
- 刪除緩存
先更新數(shù)據(jù)庫,再刪除緩存。
會有這樣一種情況:緩存剛好失效,請求B從數(shù)據(jù)庫中查詢數(shù)據(jù),得到舊值。此時請求A更新數(shù)據(jù)庫,將新值寫入數(shù)據(jù)庫,并刪除緩存。而請求B又將舊值寫入緩存中,導(dǎo)致臟數(shù)據(jù)
從上面看出現(xiàn)臟數(shù)據(jù)的要求要比更新緩存的要求更多,必須滿足以下幾個條件:
- 緩存失效
- 讀請求 + 寫請求并發(fā)
- 更新數(shù)據(jù)庫 + 刪除緩存的時間要比讀數(shù)據(jù)庫 + 寫緩存時間短
前面兩個很好滿足,我們再看看第三點(diǎn),這個真的會出現(xiàn)嗎?
數(shù)據(jù)庫在更新時一般是加鎖的,讀操作的速度遠(yuǎn)快于寫操作的,所以第三點(diǎn)發(fā)生概率極低(當(dāng)然也可能發(fā)生)
注:這里我其實(shí)不是很理解,單純看確實(shí)發(fā)生概率低,但如果出現(xiàn)網(wǎng)絡(luò)延遲等情況呢,不也會發(fā)生嗎?希望好心人解惑,我反正沒理解。
因此,在選擇刪除緩存時,還需要結(jié)合其他技術(shù)來優(yōu)化性能和一致性。例如:
- 使用消息隊列來異步地刪除或更新緩存,避免阻塞主線程或者丟失消息。
- 使用延時雙刪來增加刪除成功率和減少不一致時間窗口。即在更新數(shù)據(jù)庫后立即刪除一次緩存,并在一定時間間隔后再次刪除一次。
對比
在更新緩存中, 每次去更新緩存,但是緩存中的數(shù)據(jù)不一定會被馬上讀取,這就會導(dǎo)致緩存中可能存放了很多不常訪問的數(shù)據(jù),浪費(fèi)緩存資源。而且很多情況下,寫到緩存中的值,并不是與數(shù)據(jù)庫中的值一一對應(yīng)的,很有可能是先查詢數(shù)據(jù)庫,再經(jīng)過一系列「計算」得出一個值,才把這個值才寫到緩存中。
? 由此可見,這種更新緩存的方案,不僅緩存利用率不高,還會造成機(jī)器性能的浪費(fèi)。所以我們一般考慮刪除緩存
先更新緩存再更新數(shù)據(jù)庫
在更新數(shù)據(jù)時,先將新數(shù)據(jù)寫入緩存(Redis),再將新數(shù)據(jù)寫入數(shù)據(jù)庫(MySQL)
但其存在一下問題:
- 緩存更新成功,但數(shù)據(jù)庫更新失敗,導(dǎo)致數(shù)據(jù)不一致
例:用戶修改了自己的昵稱,系統(tǒng)先將新的昵稱寫入緩存,然后再更新數(shù)據(jù)庫。但是在更新數(shù)據(jù)庫的過程中,發(fā)生了網(wǎng)絡(luò)故障或者數(shù)據(jù)庫宕機(jī)等異常情況,導(dǎo)致數(shù)據(jù)庫中的昵稱沒有被修改。這樣就會出現(xiàn)緩存中的昵稱和數(shù)據(jù)庫中的昵稱不一致的情況。
- 緩存更新成功,但數(shù)據(jù)庫更新延遲,導(dǎo)致其他請求讀取到舊的數(shù)據(jù)
例:用戶下單了一個商品,系統(tǒng)先將訂單狀態(tài)寫入緩存,然后再更新數(shù)據(jù)庫。但是在更新數(shù)據(jù)庫的過程中,由于并發(fā)量大或者其他原因,導(dǎo)致數(shù)據(jù)庫的寫入速度慢于緩存的寫入速度。這樣就會出現(xiàn)其他請求從緩存中讀取到訂單狀態(tài)為已支付,而從數(shù)據(jù)庫中讀取到訂單狀態(tài)為未支付的情況。
- 緩存更新成功,但在數(shù)據(jù)庫更新之前有其他請求查詢了緩存和數(shù)據(jù)庫,并將舊的數(shù)據(jù)寫回緩存,覆蓋了新的數(shù)據(jù)
例:用戶A修改了自己的頭像,并上傳到服務(wù)器上。系統(tǒng)先將新的頭像地址寫入緩存,并返回給用戶A顯示。然后再將新的頭像地址更新到數(shù)據(jù)庫中。但是在這個過程中,用戶B訪問了用戶A的個人主頁,并從緩存中讀取到了新的頭像地址。由于緩存失效策略或者其他原因(比如重啟),導(dǎo)致緩存被清空或者過期。這時候用戶B再次訪問用戶A 的個人主頁,并從數(shù)據(jù)庫中讀取到了舊的頭像地址,并將其寫回緩存中。這樣就會出現(xiàn)緩存中 的頭像地址和 數(shù)據(jù)庫 中 的頭像地址不一致 的情況。
上面說了一堆,其實(shí)總結(jié)就是緩存更新成功了,數(shù)據(jù)庫沒更新(更新失敗),導(dǎo)致緩存存的是最新值,數(shù)據(jù)庫存的是舊值。如果緩存失效了,就會拿到數(shù)據(jù)庫中的舊值。
后面我自己也搞疑惑了,既然是因為數(shù)據(jù)庫更新失敗導(dǎo)致的問題,那我是不是只要保證數(shù)據(jù)庫更新成功就可以解決數(shù)據(jù)不一致的問題,當(dāng)數(shù)據(jù)庫更新失敗時,不停的重試更新數(shù)據(jù)庫,直到數(shù)據(jù)庫更新完成。
后面發(fā)現(xiàn)自己太天真,其中存在很多問題,比如:
- 如果數(shù)據(jù)庫更新失敗的原因是數(shù)據(jù)庫宕機(jī)或者網(wǎng)絡(luò)故障,那么你不停地重試更新數(shù)據(jù)庫可能會造成更大的壓力和延遲,甚至導(dǎo)致數(shù)據(jù)庫恢復(fù)困難。
- 如果數(shù)據(jù)庫更新失敗的原因是數(shù)據(jù)沖突或者業(yè)務(wù)邏輯錯誤,那么你不停地重試更新數(shù)據(jù)庫可能會導(dǎo)致數(shù)據(jù)丟失或者數(shù)據(jù)錯亂,甚至影響其他用戶的數(shù)據(jù)。
- 如果你不停地重試更新數(shù)據(jù)庫,那么你需要考慮如何保證重試的冪等性和順序性,以及如何處理重試過程中發(fā)生的異常情況。
所以,這種方法并不是一個很好的解決方案。
先更新數(shù)據(jù)庫,再更新緩存
當(dāng)有一個更新操作時,先更新數(shù)據(jù)庫數(shù)據(jù),然后再更新對應(yīng)的緩存數(shù)據(jù)
但是,這種方案也有一些問題和風(fēng)險,比如:
- 如果更新數(shù)據(jù)庫成功了,但是更新緩存失敗了,那么就會導(dǎo)致緩存中就會保留舊的數(shù)據(jù),而數(shù)據(jù)庫中已經(jīng)是新的數(shù)據(jù),即臟數(shù)據(jù)。
- 如果在更新數(shù)據(jù)庫和更新緩存之間,有其他請求查詢了同一個數(shù)據(jù),并且發(fā)現(xiàn)緩存存在,那么就會從緩存中讀取舊的數(shù)據(jù)。這樣也會造成緩存和數(shù)據(jù)庫之間的不一致性。
因此,在使用更新緩存操作時,無論誰先誰后,但凡后者發(fā)生異常,就會對業(yè)務(wù)造成影響。(還是上面那張圖)
那么如何處理異常情況來保證數(shù)據(jù)一致性呢
這些問題的源頭都是多線程并發(fā)所導(dǎo)致的,所以最簡單的方法就是加鎖(分布式鎖)。兩個線程要修改同一條數(shù)據(jù),每個線程在改之前,先去申請分布式鎖,拿到鎖的線程才允許更新數(shù)據(jù)庫和緩存,拿不到鎖的線程,返回失敗,等待下次重試。這么做的目的,就是為了只允許一個線程去操作數(shù)據(jù)和緩存,避免并發(fā)問題。
? 但加鎖費(fèi)時費(fèi)力,肯定不推薦。并且,每次去更新緩存,但是緩存中的數(shù)據(jù)不一定會被馬上讀取,這就會導(dǎo)致緩存中可能存放了很多不常訪問的數(shù)據(jù),浪費(fèi)緩存資源。而且很多情況下,寫到緩存中的值,并不是與數(shù)據(jù)庫中的值一一對應(yīng)的,很有可能是先查詢數(shù)據(jù)庫,再經(jīng)過一系列「計算」得出一個值,才把這個值才寫到緩存中。
? 由此可見,這種更新數(shù)據(jù)庫 + 更新緩存的方案,不僅緩存利用率不高,還會造成機(jī)器性能的浪費(fèi)。
所以此時我們需要考慮另外一種方案:刪除緩存
先刪除緩存再更新數(shù)據(jù)庫
當(dāng)有一個更新操作時,先刪除對應(yīng)的緩存數(shù)據(jù),然后再更新數(shù)據(jù)庫數(shù)據(jù)
但是,這種方案也有一些問題和風(fēng)險,比如:
- 如果刪除緩存后,更新數(shù)據(jù)庫失敗了,那么就會導(dǎo)致緩存丟失,下次查詢時需要重新從數(shù)據(jù)庫加載數(shù)據(jù),增加了數(shù)據(jù)庫壓力和響應(yīng)時間。
- 如果在刪除緩存和更新數(shù)據(jù)庫之間,有其他請求查詢了同一個數(shù)據(jù),并且發(fā)現(xiàn)緩存不存在,那么就會從數(shù)據(jù)庫中讀取舊的數(shù)據(jù),并寫入到緩存中。這樣就會造成緩存和數(shù)據(jù)庫之間的不一致性。
先更新數(shù)據(jù)庫,再刪除緩存
當(dāng)有一個更新操作時,先更新數(shù)據(jù)庫數(shù)據(jù),再刪除緩存
上面其實(shí)講過了,我再重復(fù)一遍吧
會有這樣一種情況:緩存剛好失效,請求B從數(shù)據(jù)庫中查詢數(shù)據(jù),得到舊值。此時請求A更新數(shù)據(jù)庫,將新值寫入數(shù)據(jù)庫,并刪除緩存。而請求B又將舊值寫入緩存中,導(dǎo)致臟數(shù)據(jù)
從上面看出現(xiàn)臟數(shù)據(jù)的要求要比更新緩存的要求更多,必須滿足以下幾個條件:
- 緩存失效
- 讀請求 + 寫請求并發(fā)
- 更新數(shù)據(jù)庫 + 刪除緩存的時間要比讀數(shù)據(jù)庫 + 寫緩存時間短
前面兩個很好滿足,我們再看看第三點(diǎn),這個真的會出現(xiàn)嗎?
數(shù)據(jù)庫在更新時一般是加鎖的,讀操作的速度遠(yuǎn)快于寫操作的,所以第三點(diǎn)發(fā)生概率極低
所以,解決雙寫問題更適合的方法是先更新數(shù)據(jù)庫,再刪除緩存,當(dāng)然具體場景具體分析,不定說一定就是這個。
講解了這些操作后會出現(xiàn)的問題,那么為了避免這些問題,如何做呢?
- 先刪除緩存再更新數(shù)據(jù)庫,然后使用異步線程或消息隊列來重建緩存。
- 先更新數(shù)據(jù)庫再刪除緩存,并設(shè)置一個合理的過期時間來保證緩存的有效性。
- 使用分布式鎖或樂觀鎖來控制并發(fā)訪問,并保證每次只有一個請求能夠操作緩存和數(shù)據(jù)庫
……
下面講幾種常見的方法以保證雙寫一致性
解決方案
1. 重試
上面也提到過,當(dāng)?shù)诙讲僮魇r,我就重試嘛,盡可能地補(bǔ)救,但重試的成本太大,上面講過就不重復(fù)了。
2. 異步重試
? 既然重試方法占用資源,那我就做異步。在刪除或更新緩存時,如果操作失敗,不立即返回錯誤,而是通過一些機(jī)制(如消息隊列、定時任務(wù)、訂閱binlog等)來觸發(fā)緩存的重試操作。這樣可以避免同步重試緩存時的性能損耗和阻塞問題,但也可能導(dǎo)致緩存和數(shù)據(jù)庫的數(shù)據(jù)不一致的時間較長。
2.1 使用消息隊列實(shí)現(xiàn)重試
- 消息隊列保證可靠性:寫到隊列中的消息,成功消費(fèi)之前不會丟失(重啟項目也不擔(dān)心)
- 消息隊列保證消息成功投遞:下游從隊列拉取消息,成功消費(fèi)后才會刪除消息,否則還會繼續(xù)投遞消息給消費(fèi)者(符合我們重試的需求)
使用消息隊列異步重試緩存的情況是指,當(dāng)信息發(fā)生變化時,先更新數(shù)據(jù)庫,然后刪緩存,如果刪除成功就皆大歡喜,如果刪除失敗,則將需要刪除的key發(fā)送到消息隊列。另外有一個消費(fèi)者線程從消息隊列中獲取要刪除的key,并根據(jù)key刪除或更新Redis中的緩存。如果操作失敗,則重新發(fā)送到消息隊列中進(jìn)行重試。
注:也可以不先嘗試刪除,直接發(fā)送給消息隊列,讓消息隊列
舉例來說,假設(shè)有一個用戶信息表,需要將用戶信息緩存在Redis中。如果采用使用消息隊列異步重試緩存的方案,可以有以下幾個步驟:
- 當(dāng)用戶信息發(fā)生變化時,先更新數(shù)據(jù)庫,并返回成功結(jié)果給前端。
- 嘗試去刪除緩存,成功則結(jié)束操作,失敗則將要刪除或更新緩存的操作生成一個消息(比如包含key和操作類型),并發(fā)送到消息隊列中(比如使用Kafka或RabbitMQ)。
- 另外有一個消費(fèi)者線程從消息隊列中訂閱并獲取這些消息,并根據(jù)消息內(nèi)容刪除或更新Redis中的對應(yīng)信息。
- 如果刪除或更新緩存成功,則把這個消息從消息隊列中移除(丟棄),以免重復(fù)操作。
- 如果刪除或更新緩存失敗,則執(zhí)行失敗策略,比如設(shè)置一個延遲時間或者一個重試次數(shù)限制,然后重新發(fā)送這個消息到消息隊列中進(jìn)行重試。
- 如果重試超過一定次數(shù)仍然失敗,則向業(yè)務(wù)層發(fā)送報錯信息,并記錄日志。
2.2 Binlog實(shí)現(xiàn)異步重試刪除
使用binlog實(shí)現(xiàn)一致性的基本思路是利用binlog日志來記錄數(shù)據(jù)庫的變更操作,然后通過主從復(fù)制或者增量備份的方式來同步或者恢復(fù)數(shù)據(jù)。
舉例來說,如果我們有一個主數(shù)據(jù)庫和一個從數(shù)據(jù)庫,我們可以在主數(shù)據(jù)庫上開啟binlog日志,并設(shè)置從數(shù)據(jù)庫作為它的復(fù)制節(jié)點(diǎn)。這樣,當(dāng)主數(shù)據(jù)庫上發(fā)生任何變更操作時,它會將對應(yīng)的binlog日志發(fā)送給從數(shù)據(jù)庫,從數(shù)據(jù)庫則會根據(jù)binlog日志來執(zhí)行相同的操作,從而保證數(shù)據(jù)一致性。
另外,如果我們需要恢復(fù)某個時間點(diǎn)之前的數(shù)據(jù),我們也可以利用binlog日志來實(shí)現(xiàn)。首先,我們需要找到對應(yīng)時間點(diǎn)之前的最近一個全量備份文件,并將其恢復(fù)到目標(biāo)數(shù)據(jù)庫。然后,我們需要找到對應(yīng)時間點(diǎn)之前的所有增量備份文件(即binlog日志文件),并按照順序?qū)⑵鋺?yīng)用到目標(biāo)數(shù)據(jù)庫。這樣,我們就可以恢復(fù)出目標(biāo)時間點(diǎn)之前的數(shù)據(jù)狀態(tài)了。
- 使用 Binlog 實(shí)時更新/刪除 Redis 緩存。利用 Canal,即將負(fù)責(zé)更新緩存的服務(wù)偽裝成一個 MySQL 的從節(jié)點(diǎn),從 MySQL 接收 Binlog,解析 Binlog 之后,得到實(shí)時的數(shù)據(jù)變更信息,然后根據(jù)變更信息去更新/刪除 Redis 緩存;
- MQ+Canal 策略,將 Canal Server 接收到的 Binlog 數(shù)據(jù)直接投遞到 MQ 進(jìn)行解耦,使用 MQ 異步消費(fèi) Binlog 日志,以此進(jìn)行數(shù)據(jù)同步;
注:binlog日志是MySQL的二進(jìn)制日志,它記錄了對數(shù)據(jù)庫的變更操作,比如插入、更新、刪除等。 binlog日志有兩個主要作用,一個是主從復(fù)制,另一個是增量備份。
主從復(fù)制是指在一個主數(shù)據(jù)庫和一個或多個從數(shù)據(jù)庫之間實(shí)現(xiàn)數(shù)據(jù)的同步。主數(shù)據(jù)庫會將自己的binlog日志發(fā)送給從數(shù)據(jù)庫,從數(shù)據(jù)庫則會根據(jù)binlog日志來執(zhí)行相同的操作,從而保證數(shù)據(jù)一致性。這樣可以提高數(shù)據(jù)的可用性和可靠性,也可以實(shí)現(xiàn)負(fù)載均衡和故障轉(zhuǎn)移。
增量備份是指在全量備份的基礎(chǔ)上,定期備份數(shù)據(jù)庫的變更操作。全量備份是指將整個數(shù)據(jù)庫的數(shù)據(jù)完整地備份到一個文件中。增量備份則是指將每次變更操作對應(yīng)的binlog日志文件備份到另一個文件中。這樣可以減少備份所占用的空間和時間,也可以實(shí)現(xiàn)靈活地恢復(fù)數(shù)據(jù)到任意時間點(diǎn)。
至此,我們可以得出結(jié)論,想要保證數(shù)據(jù)庫和緩存一致性,推薦采用「先更新數(shù)據(jù)庫,再刪除緩存」方案,并配合「消息隊列」或「訂閱變更日志」的方式來做。
3. 延時雙刪
我們重點(diǎn)在將先更新數(shù)據(jù)庫,在刪除緩存。那如果我要先刪除緩存,再更新數(shù)據(jù)庫呢?
回顧之前講的先刪除緩存,再更新數(shù)據(jù)庫,它會出現(xiàn)舊值覆蓋緩存的問題,那好辦,我們直接把這個舊值給刪了不就完了嗎,延時雙刪就是這個原理,它的基本思路是:
- 先刪除緩存
- 再更新數(shù)據(jù)庫
- 休眠一段時間(根據(jù)系統(tǒng)情況確定)
- 再次刪除緩存
這樣做的目的是為了防止在更新數(shù)據(jù)庫后,有其他線程讀取到舊的緩存數(shù)據(jù),并將其寫回緩存,導(dǎo)致數(shù)據(jù)不一致。
舉個例子:假設(shè)有一個用戶信息表,其中有一個字段是用戶積分?,F(xiàn)在有兩個線程A和B同時對用戶積分進(jìn)行操作:
- 線程A要給用戶增加100積分
- 線程B要給用戶減少50積分
如果使用延時雙刪策略,那么線程A和B的執(zhí)行過程可能如下:
- 線程A先刪除緩存中的用戶信息
- 線程A再從數(shù)據(jù)庫中讀取用戶信息,發(fā)現(xiàn)用戶積分為1000
- 線程A將用戶積分加上100,變?yōu)?100,并更新到數(shù)據(jù)庫中
- 線程A休眠5秒(假設(shè)這個時間足夠讓數(shù)據(jù)庫同步)
- 線程A再次刪除緩存中的用戶信息
- 線程B先刪除緩存中的用戶信息
- 線程B再從數(shù)據(jù)庫中讀取用戶信息,發(fā)現(xiàn)用戶積分為1100(因為線程A已經(jīng)更新了)
- 線程B將用戶積分減去50,變?yōu)?050,并更新到數(shù)據(jù)庫中
- 線程B休眠5秒(假設(shè)這個時間足夠讓數(shù)據(jù)庫同步)
- 線程B再次刪除緩存中的用戶信息
這樣最終結(jié)果就是:數(shù)據(jù)庫中的用戶積分為1050,緩存中沒有該用戶信息。當(dāng)下次有請求查詢該用戶信息時,就會從數(shù)據(jù)庫中讀取并寫入到緩存中。這樣就保證了數(shù)據(jù)一致性。
延時雙刪適用于高并發(fā)場景,特別是對數(shù)據(jù)的修改操作比較頻繁,而查詢操作比較少的情況。這樣可以減輕數(shù)據(jù)庫的壓力,提高性能,同時保證數(shù)據(jù)的最終一致性。延時雙刪也適用于數(shù)據(jù)庫有主從同步延遲的場景,因為它可以避免在更新數(shù)據(jù)庫后,從庫還沒有同步完成時,讀取到舊的緩存數(shù)據(jù),并將其寫回緩存。
注: 這個休眠時間 = 讀業(yè)務(wù)邏輯數(shù)據(jù)的耗時 + 幾百毫秒。 為了確保讀請求結(jié)束,寫請求可以刪除讀請求可能帶來的緩存臟數(shù)據(jù)。
總結(jié)
好了,總結(jié)一下這篇文章的重點(diǎn)。
Redis與MySQL的雙寫一致性問題是指在使用緩存和數(shù)據(jù)庫同時存儲數(shù)據(jù)的場景下,如何保證兩者的數(shù)據(jù)一致性。這個問題主要涉及到以下幾個方面:
- 緩存更新策略:緩存更新策略有三種,分別是先更新緩存再更新數(shù)據(jù)庫,先更新數(shù)據(jù)庫再更新緩存,先刪除緩存再更新數(shù)據(jù)庫 和先更新數(shù)據(jù)庫再刪除緩存。每種策略都有可能導(dǎo)致數(shù)據(jù)不一致的情況。
- 數(shù)據(jù)庫主從同步延遲:如果使用了主從復(fù)制模式來提高數(shù)據(jù)庫的可用性和讀寫分離能力,那么就可能存在主從同步延遲的問題。也就是說,在主庫上執(zhí)行了寫操作后,并不會立即同步到從庫上。這樣,在讀取數(shù)據(jù)時,如果從主庫讀取,則可能獲取到最新的數(shù)據(jù);而如果從從庫讀取,則可能獲取到舊的數(shù)據(jù)。這樣也會導(dǎo)致與緩存中的數(shù)據(jù)不一致。
為了解決這些問題 , 可以采用以下幾種方法 :
- 采用先刪除緩存,再更新數(shù)據(jù)庫方案,在并發(fā)場景下依舊有不一致問題,解決方案是延遲雙刪,但這個延遲時間很難評估。
- 采用先更新數(shù)據(jù)庫,再刪除緩存方案,為了保證兩步都成功執(zhí)行,需配合消息隊列或訂閱變更日志的方案來做,本質(zhì)是通過重試的方式保證數(shù)據(jù)最終一致性。
- 采用先更新數(shù)據(jù)庫,再刪除緩存方案,讀寫分離 + 主從庫延遲也會導(dǎo)致緩存和數(shù)據(jù)庫不一致,緩解此問題的方案是延遲雙刪,憑借經(jīng)驗發(fā)送延遲消息到隊列中,延遲刪除緩存,同時也要控制主從庫延遲,盡可能降低不一致發(fā)生的概率。
總之,根據(jù)場景選擇適合自己的方案
以上就是Redis與MySQL的雙寫一致性問題的詳細(xì)內(nèi)容,更多關(guān)于Redis與MySQL的一致性的資料請關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
mysql Access denied for user ‘root’@’localhost’ (using passw
這篇文章主要介紹了mysql Access denied for user ‘root’@’localhost’ (using password: YES)解決方法,本文給出詳細(xì)的解決步驟及操作注釋,需要的朋友可以參考下2015-07-07MySQL execute、executeUpdate、executeQuery三者的區(qū)別
這篇文章主要介紹了MySQL execute、executeUpdate、executeQuery三者的區(qū)別的相關(guān)資料,需要的朋友可以參考下2017-05-05