目錄
一、什么是緩存雙寫(xiě)一致性呢?
?1.1 雙檢加鎖機(jī)制
?二、數(shù)據(jù)庫(kù)和緩存一致性的更新策略
2.1、先更新數(shù)據(jù)庫(kù),后更新緩存
?2.2 、先更新緩存,后更新數(shù)據(jù)庫(kù)
?2.3、先刪除緩存,在更新數(shù)據(jù)庫(kù)
延時(shí)雙刪的策略:
?2.4.先更新數(shù)據(jù)庫(kù),在刪除緩存(常用)
2.5、實(shí)際中是不可能做到強(qiáng)一致性的,那么怎么做到最終一致性呢?
三、canal中間件
3.1? ?canal工作原理
3.2 MySQL的主從復(fù)制?
一、什么是緩存雙寫(xiě)一致性呢?
-
如果redis中有數(shù)據(jù)
- 需要和數(shù)據(jù)庫(kù)中的值相同
-
如果redis中無(wú)數(shù)據(jù)
- 數(shù)據(jù)庫(kù)中的值是最新值,且準(zhǔn)備回寫(xiě)redis
緩存按照操作分
- 只讀緩存? ? (就沒(méi)有同步這一說(shuō)法了)
- 讀寫(xiě)緩存
-
同步直寫(xiě)策略? ?(比如比較緊急的事情,沖了vip得立即生效)
- 寫(xiě)數(shù)據(jù)庫(kù)后也同步寫(xiě) redis 緩存,緩存中的數(shù)據(jù)和數(shù)據(jù)庫(kù)中的一致
- 對(duì)于讀寫(xiě)緩存來(lái)說(shuō),要想保證緩存和數(shù)據(jù)庫(kù)中的數(shù)據(jù)一致
-
同步直寫(xiě)策略? ?(比如比較緊急的事情,沖了vip得立即生效)
-
異步緩寫(xiě)策略? ?(一般都是用這種)
- 正常業(yè)務(wù)運(yùn)行中,mysql數(shù)據(jù)變動(dòng)了,但是可以在業(yè)務(wù)上容許出現(xiàn)一定時(shí)間后才作用于redis,比如倉(cāng)庫(kù)、物流系統(tǒng)
- 異常情況出現(xiàn)了,不得不將失敗的動(dòng)作重新修補(bǔ),有可能需要借助kafka或者RabbitMQ等消息中間件,實(shí)現(xiàn)重寫(xiě)重試
?1.1 雙檢加鎖機(jī)制
加鎖前從redis中查一次,加鎖后再查一次。
多個(gè)線程同時(shí)去查詢數(shù)據(jù)庫(kù)的這條數(shù)據(jù),那么我們可以在第一個(gè)查詢數(shù)據(jù)的請(qǐng)求上使用一個(gè) 互斥鎖來(lái)鎖住它。
其他的線程走到這一步拿不到鎖就等著,等第一個(gè)線程查詢到了數(shù)據(jù),然后做緩存。
后面的線程進(jìn)來(lái)發(fā)現(xiàn)已經(jīng)有緩存了,就直接走緩存。?
?二、數(shù)據(jù)庫(kù)和緩存一致性的更新策略
一般都是以MySQL為準(zhǔn)。
給緩存設(shè)置過(guò)期時(shí)間,定期清理緩存并回寫(xiě),是保證最終一致性的解決方案。
我們可以對(duì)存入緩存的數(shù)據(jù)設(shè)置過(guò)期時(shí)間,所有的寫(xiě)操作以數(shù)據(jù)庫(kù)為準(zhǔn),對(duì)緩存操作只是盡最大努力即可。也就是說(shuō)如果數(shù)據(jù)庫(kù)寫(xiě)成功,緩存更新失敗,那么只要到達(dá)過(guò)期時(shí)間,則后面的讀請(qǐng)求自然會(huì)從數(shù)據(jù)庫(kù)中讀取新值然后回填緩存,達(dá)到一致性,切記,要以mysql的數(shù)據(jù)庫(kù)寫(xiě)入庫(kù)為準(zhǔn)
2.1、先更新數(shù)據(jù)庫(kù),后更新緩存
異常一:? ? ? ? 回寫(xiě)失敗會(huì)出現(xiàn)臟數(shù)據(jù)
?異常二:? ? ? ?高并發(fā)下會(huì)出現(xiàn)數(shù)據(jù)覆蓋
?2.2 、先更新緩存,后更新數(shù)據(jù)庫(kù)
我們一般是不用這種的,因?yàn)槲覀円话愣及袽ySQL作為根基
異常:? ? ? ?高并發(fā)下會(huì)出現(xiàn)數(shù)據(jù)覆蓋
?2.3、先刪除緩存,在更新數(shù)據(jù)庫(kù)
當(dāng)有兩個(gè)線程:一個(gè)線程負(fù)責(zé)刪Redis,修改MySQL,? 另一個(gè)來(lái)查找redis
?如果數(shù)據(jù)庫(kù)更新失敗或者不及時(shí)就會(huì)發(fā)生異常:
(1)請(qǐng)求A進(jìn)行寫(xiě)操作,刪除redis緩存后,工作正在進(jìn)行中,更新mysql......A還沒(méi)有徹底更新完mysql,還沒(méi)commit
(2)請(qǐng)求B開(kāi)工查詢,查詢r(jià)edis發(fā)現(xiàn)緩存不存在(被A從redis中刪除了)
(3)請(qǐng)求B繼續(xù),去數(shù)據(jù)庫(kù)查詢得到了mysql中的舊值(A還沒(méi)有更新完)
(4)請(qǐng)求B將舊值回寫(xiě)redis緩存
(5)請(qǐng)求A將新值寫(xiě)入mysql數(shù)據(jù)庫(kù)?
上述情況就會(huì)導(dǎo)致不一致的情形出現(xiàn)。?
時(shí)間
|
線程A
|
線程B
|
出現(xiàn)的問(wèn)題
|
t1
|
請(qǐng)求A進(jìn)行寫(xiě)操作,刪除緩存成功后,工作正在mysql進(jìn)行中......
|
||
t2
|
1 緩存中讀取不到,立刻讀mysql,由于A還沒(méi)有對(duì)mysql更新完,讀到的是舊值
2 還把從mysql讀取的舊值,寫(xiě)回了redis
|
1 A還沒(méi)有更新完mysql,導(dǎo)致B讀到了舊值
2 線程B遵守回寫(xiě)機(jī)制,把舊值寫(xiě)回redis,導(dǎo)致其它請(qǐng)求讀取的還是舊值,A白干了。
|
|
t3
|
A更新完mysql數(shù)據(jù)庫(kù)的值,over
|
redis是被B寫(xiě)回的舊值,
mysql是被A更新的新值。
出現(xiàn)了,數(shù)據(jù)不一致問(wèn)題。
|
總結(jié)一下:
先刪除緩存,再更新數(shù)據(jù)庫(kù)
|
如果數(shù)據(jù)庫(kù)更新失敗或超時(shí)或返回不及時(shí),導(dǎo)致B線程請(qǐng)求訪問(wèn)緩存時(shí)發(fā)現(xiàn)redis里面沒(méi)數(shù)據(jù),緩存缺失,B再去讀取mysql時(shí),
從數(shù)據(jù)庫(kù)中讀取到舊值,還寫(xiě)回redis,導(dǎo)致A白干了,o(╥﹏╥)o
|
改怎么解決呢?
延時(shí)雙刪的策略:
?這個(gè)刪除該休眠多久呢?
?因?yàn)檫@種同步淘汰機(jī)制加上了sleep,導(dǎo)致MySQL吞吐量降低怎么辦?
?
?2.4.先更新數(shù)據(jù)庫(kù),在刪除緩存(常用)
?這一種方法的弊端相對(duì)比較少
時(shí)間
|
線程A
|
線程B
|
出現(xiàn)的問(wèn)題
|
t1
|
更新數(shù)據(jù)庫(kù)中的值......
|
||
t2
|
?緩存中立刻命中,此時(shí)B讀取的是緩存舊值。
|
A還沒(méi)有來(lái)得及刪除緩存的值,導(dǎo)致B緩存命中讀到舊值。 | |
t3
|
更新緩存的數(shù)據(jù),over
|
先更新數(shù)據(jù)庫(kù),再刪除緩存
|
假如緩存刪除失敗或者來(lái)不及,導(dǎo)致請(qǐng)求再次訪問(wèn)redis時(shí)緩存命中,
讀取到的是緩存舊值。
|
2.5、實(shí)際中是不可能做到強(qiáng)一致性的,那么怎么做到最終一致性呢?
需要用到消息隊(duì)列:kafka或者RabbitMQ
但是還是都需要是先更新數(shù)據(jù)庫(kù),再刪除緩存,這樣最多也就是數(shù)據(jù)暫時(shí)不一致,不會(huì)導(dǎo)致雪崩、擊穿啥的出現(xiàn)。
1 可以把要?jiǎng)h除的緩存值或者是要更新的數(shù)據(jù)庫(kù)值暫存到消息隊(duì)列中(例如使用Kafka/RabbitMQ等)。
2 當(dāng)程序沒(méi)有能夠成功地刪除緩存值或者是更新數(shù)據(jù)庫(kù)值時(shí),可以從消息隊(duì)列中重新讀取這些值,然后再次進(jìn)行刪除或更新。
3 如果能夠成功地刪除或更新,我們就要把這些值從消息隊(duì)列中去除,以免重復(fù)操作,此時(shí),我們也可以保證數(shù)據(jù)庫(kù)和緩存的數(shù)據(jù)一致了,否則還需要再次進(jìn)行重試
4 如果重試超過(guò)的一定次數(shù)后還是沒(méi)有成功,我們就需要向業(yè)務(wù)層發(fā)送報(bào)錯(cuò)信息了,通知運(yùn)維人員。
|
三、canal中間件
能夠立刻感知到MySQL改變的有一個(gè)MySQL的binlog文件
我們需要一種技術(shù)來(lái)充當(dāng)兩者之前的吹哨人
這里有阿里研發(fā)的一種中間件canal
3.1? ?canal工作原理
1.?canal 模仿MySQL的dump協(xié)議,假裝自己是MySQL的slave,向MySQL發(fā)送dump協(xié)議
2. MySQLmaster收到dump請(qǐng)求之后,便會(huì)給canal推送自身bin? log 變化給canal
3. cannal收到bin? log 消息并解析。
3.2 MySQL的主從復(fù)制?
MySQL的主從復(fù)制將經(jīng)過(guò)如下步驟:
1、當(dāng) master 主服務(wù)器上的數(shù)據(jù)發(fā)生改變時(shí),則將其改變寫(xiě)入二進(jìn)制事件日志文件中;
2、salve 從服務(wù)器會(huì)在一定時(shí)間間隔內(nèi)對(duì) master 主服務(wù)器上的二進(jìn)制日志進(jìn)行探測(cè),探測(cè)其是否發(fā)生過(guò)改變,
如果探測(cè)到 master 主服務(wù)器的二進(jìn)制事件日志發(fā)生了改變,則開(kāi)始一個(gè) I/O Thread 請(qǐng)求 master 二進(jìn)制事件日志;
3、同時(shí) master 主服務(wù)器為每個(gè) I/O Thread 啟動(dòng)一個(gè)dump? Thread,用于向其發(fā)送二進(jìn)制事件日志;
4、slave 從服務(wù)器將接收到的二進(jìn)制事件日志保存至自己本地的中繼日志文件中;
5、salve 從服務(wù)器將啟動(dòng) SQL Thread 從中繼日志中讀取二進(jìn)制日志,在本地重放,使得其數(shù)據(jù)和主服務(wù)器保持一致;文章來(lái)源:http://www.zghlxwxcb.cn/news/detail-553181.html
6、最后 I/O Thread 和 SQL Thread 將進(jìn)入睡眠狀態(tài),等待下一次被喚醒;文章來(lái)源地址http://www.zghlxwxcb.cn/news/detail-553181.html
到了這里,關(guān)于Redis---緩存雙寫(xiě)一致性的文章就介紹完了。如果您還想了解更多內(nèi)容,請(qǐng)?jiān)谟疑辖撬阉鱐OY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!