如何保證redis和MySQL數(shù)據(jù)的一致性
首先說結(jié)論:無法保證100%的數(shù)據(jù)強(qiáng)一致性,只要是雙寫,悲觀角度看待一定會出現(xiàn)數(shù)據(jù)不一致問題,這個就好比一個人又要向左看又要向右看一樣,是不可能的,除非你有影分身
介紹4種思路
1 先更新數(shù)據(jù)庫,再更新緩存
適用場景:服務(wù)器停機(jī)狀態(tài)下可以使用這種方法
在不停機(jī)的情況下
可能出現(xiàn)的問題:1?MySQL更新成功 redis失敗
舉例:先更新了MySQL,假設(shè)數(shù)量從100改為99,然后回寫進(jìn)redis的時候發(fā)生了異常,導(dǎo)致MySQL內(nèi)是99,redis內(nèi)還是100,下次讀取操作會命中redis,將100返回導(dǎo)致數(shù)據(jù)不一致
可能出現(xiàn)的問題:2 多線程環(huán)境下,每個線程的執(zhí)行消耗時間不一致,導(dǎo)致出現(xiàn)問題
舉例:線程A在更新數(shù)據(jù)庫的時候B線程也要更新同一個數(shù)據(jù)此時發(fā)生了如下情況
現(xiàn)在要把數(shù)據(jù)從100改為99
A線程帶著99正在走路去更新的路上...
需求緊急變更,要改為98
B線程火速坐飛機(jī)帶著98殺進(jìn)來更新
B線程更新完了,回寫進(jìn)redis,任務(wù)完成
A線程終于走到站了,把更新的數(shù)據(jù)寫入MySQL
A線程回寫進(jìn)redis,任務(wù)完成
B表示???更新了個寂寞,白干了
不推薦

2 先更新緩存,再更新數(shù)據(jù)庫
不推薦 因為一般會把數(shù)據(jù)庫最為最終解釋數(shù)據(jù),不會把緩存數(shù)據(jù)當(dāng)作最終數(shù)據(jù)
正常邏輯:
A更新redis
A更新MySQL
B更新redis
B更新MySQL
多線程情況下可能會出現(xiàn)這個問題
數(shù)據(jù)庫內(nèi)數(shù)據(jù)為100
A更新redis? ==>99
B更新redis ===>98
B更新數(shù)據(jù)庫 ===>98
A更新數(shù)據(jù)庫 ===>99
一頓操作完成后
redis => 98
MySQL => 99
導(dǎo)致不一致
不推薦

3 先刪除緩存,在更新數(shù)據(jù)庫
可能出現(xiàn)的問題 1 A線程刪除redis,正在更新MySQL,B來讀取MySQL
此時A正在更新,B讀取到了舊的數(shù)據(jù),然后將舊數(shù)據(jù)寫入redis,導(dǎo)致A線程在MySQL中更新完后發(fā)現(xiàn)redis里面有臟數(shù)據(jù),就無動于衷,不管了
解決方案:延時雙刪
實現(xiàn)方式:
先刪除redis里面的值,然后正常執(zhí)行業(yè)務(wù)邏輯,執(zhí)行完成后再去刪除redis里面的緩存
此時,按照上面問題1 的邏輯,A先刪除redis,去更新MySQL,此時B來查找,redis無,找MySQL,找到,返回并寫入redis,B雖然返回的是舊數(shù)據(jù),但是B完事了,此時A對MySQL的操作完成了,再去刪除redis里面的緩存,就把B才寫入的舊數(shù)據(jù)又干掉了,這樣,當(dāng)C線程再來查詢的時候,redis里面沒有,去MySQL里面查詢到的就是A更新完的最新的數(shù)據(jù),然后寫入redis,就達(dá)成了redis和MySQL數(shù)據(jù)一致
但是上述實現(xiàn)方式又有一個問題
問題1
A處理業(yè)務(wù)邏輯的時間一定要大于B讀取數(shù)據(jù)并寫入redis的時間,那A要干活多久呢?
方法1?
在業(yè)務(wù)程序運行的時候,統(tǒng)部下線程讀數(shù)據(jù)和寫緩存的操作時間,自行評估自己的項目的讀數(shù)據(jù)業(yè)務(wù)邏輯的耗時,以此為基礎(chǔ)來進(jìn)行估算。然后寫數(shù)據(jù)的休眠時間則在讀數(shù)據(jù)業(yè)務(wù)邏輯的耗時基礎(chǔ)上加百毫秒即可。(我覺得不行,不要犯經(jīng)驗主義的錯誤)
方法2
新啟動一個后臺監(jiān)控程序,比如后面要講解的WatchDog監(jiān)控程序,會加時(我覺得可)
問題2
這種同步淘汰策略,吞吐量降低怎么辦?

說明:起一個異步線程刪除redis里面的數(shù)據(jù)
結(jié)論:能用,比較麻煩,建議看4

4 先更新數(shù)據(jù)庫,再刪除緩存
邏輯順序介紹:
A線程更新了MySQL,B突然來查詢,發(fā)現(xiàn)有,然后就返回了舊值,B的事情干完了,A更新MySQL完成后,去把redis緩存刪除,這樣,C線程再來讀取的時候,redis里沒有,就去MySQL內(nèi)讀取到了最新值,并寫入了redis
結(jié)論:這樣是比較穩(wěn)妥的方案,建議使用
但是還有最后一個問題:數(shù)據(jù)的最終一致性
解決方案:消息中間件



小總結(jié):

