MySQL數(shù)據(jù)庫(kù)和Redis緩存一致性的更新策略
一、更新策略
1、如果Redis中有數(shù)據(jù),需要和數(shù)據(jù)庫(kù)中的值相同。
2、如果Redis中無(wú)數(shù)據(jù),數(shù)據(jù)庫(kù)中的最新值要對(duì)Redis進(jìn)行同步更新。
二、讀寫(xiě)緩存
1、同步直寫(xiě)策略
寫(xiě)入數(shù)據(jù)庫(kù)也同步寫(xiě)Redis緩存,緩存和數(shù)據(jù)庫(kù)中的數(shù)據(jù)一致;對(duì)于讀寫(xiě)緩存來(lái)說(shuō),要保證緩存和數(shù)據(jù)庫(kù)中的數(shù)據(jù)一致,就要保證同步直寫(xiě)策略。
2、異步緩寫(xiě)策略
某些業(yè)務(wù)運(yùn)行中,MySQL數(shù)據(jù)更新之后,允許在一定時(shí)間后再進(jìn)行Redis數(shù)據(jù)同步,比如物流系統(tǒng)。
當(dāng)出現(xiàn)異常情況時(shí),不得不將失敗的動(dòng)作重新修補(bǔ),需要借助rabbitmq或kafka進(jìn)行重寫(xiě)。
三、雙檢加鎖策略
多個(gè)線程同時(shí)去查詢(xún)數(shù)據(jù)庫(kù)的這條數(shù)據(jù),那么我們可以在第一個(gè)查詢(xún)數(shù)據(jù)的請(qǐng)求上使用一個(gè) 互斥鎖來(lái)鎖住它。
其他的線程走到這一步拿不到鎖就等著,等第一個(gè)線程查詢(xún)到了數(shù)據(jù),然后做緩存。
后面的線程進(jìn)來(lái)發(fā)現(xiàn)已經(jīng)有緩存了,就直接走緩存。
public String get(String key){
// 從Redis緩存中讀取
String value = redisTemplate.get(key);
if(value != null){
return value;
}
synchronized (RedisTest.class){
// 重新嘗試從Redis緩存中讀取
value = redisTemplate.get(key);
if(value != null){
return value;
}
// 從MySQL數(shù)據(jù)庫(kù)中查詢(xún)
value = studentDao.get(key);
// 寫(xiě)入Redis緩存
redisTemplate.setnx(key,value,time);
return value;
}
}
四、數(shù)據(jù)庫(kù)和緩存一致性的更新策略
1、先更新數(shù)據(jù)庫(kù),再更新Redis
按照常理出牌的話(huà),應(yīng)該都是如此吧?那么,這種情況下,會(huì)有啥問(wèn)題呢?
如果更新數(shù)據(jù)庫(kù)成功后,更新Redis之前異常了,會(huì)出現(xiàn)什么情況呢?
數(shù)據(jù)庫(kù)與Redis內(nèi)緩存數(shù)據(jù)不一致。
2、先更新緩存,再更新數(shù)據(jù)庫(kù)
多線程情況下,會(huì)有問(wèn)題。
比如
- 線程1更新redis = 200;
- 線程2更新redis = 100;
- 線程2更新MySQL = 100;
- 線程1更新MySQL = 200;
結(jié)果呢,Redis=100、MySQL=200;我擦!
3、先刪除緩存,再更新數(shù)據(jù)庫(kù)
線程1刪除了Redis的緩存數(shù)據(jù),然后去更新MySQL數(shù)據(jù)庫(kù);
還沒(méi)等MySQL更新完畢,線程2殺來(lái),讀取緩存數(shù)據(jù);
但是,此時(shí)MySQL數(shù)據(jù)庫(kù)還沒(méi)更新,線程2讀取了MySQL中的舊值,然后線程2,還會(huì)將舊值寫(xiě)入Redis作為數(shù)據(jù)緩存;
線程1更新完MySQL數(shù)據(jù)后,發(fā)現(xiàn)Redis中已經(jīng)有數(shù)據(jù)了,之前都刪過(guò)了,那我就不更新了;
完蛋了。。
延時(shí)雙刪
延時(shí)雙刪可以解決上面的問(wèn)題,只要sleep的時(shí)間大于線程2讀取數(shù)據(jù)再寫(xiě)入緩存的時(shí)間就可以了,也就是線程1的二次清緩存操作要在線程2寫(xiě)入緩存之后,這樣才能保證Redis緩存中的數(shù)據(jù)是最新的。
/**
* 延時(shí)雙刪
* @autor 哪吒編程
*/
public void deleteRedisData(Student stu){
// 刪除Redis中的緩存數(shù)據(jù)
jedis.del(stu);
// 更新MySQL數(shù)據(jù)庫(kù)數(shù)據(jù)
studentDao.update(stu);
// 休息兩秒
try {
TimeUnit.SECONDS.sleep(2);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 刪除Redis中的緩存數(shù)據(jù)
jedis.del(stu);
}
延遲雙刪最大的問(wèn)題就是sleep,在效率為王的今天,sleep能不用還是不用為好。
你不睡我都嫌你慢,你還睡上了…
4、先更新數(shù)據(jù)庫(kù),再刪除緩存
- 線程1先更新數(shù)據(jù)庫(kù),再刪除Redis緩存;
- 線程2在線程1刪除Redis緩存之前發(fā)起請(qǐng)求,得到了未刪除的Redis緩存;
- 線程1此時(shí)才刪除Redis緩存數(shù)據(jù);
問(wèn)題還是有,這翻來(lái)覆去的,沒(méi)完沒(méi)了了。
這種情況如何解決呢?
引入消息中間件解決戰(zhàn)斗,再一次詳細(xì)的復(fù)盤(pán)一下。
- 更新數(shù)據(jù)庫(kù);
- 數(shù)據(jù)庫(kù)將操作信息寫(xiě)入binlog日志;
- 訂閱程序提取出key和數(shù)據(jù);
- 嘗試刪除緩存操作,發(fā)現(xiàn)刪除失敗;
- 將這些數(shù)據(jù)信息發(fā)送到消息中間件中;
- 從消息中間件中獲取該數(shù)據(jù),重新操作;
5、總結(jié)
哪吒推薦使用第四種方式,先更新數(shù)據(jù)庫(kù),再刪除緩存。
方式①和方式②缺點(diǎn)太過(guò)明顯,不考慮;
方式③中的sleep,總是讓人頭疼;
方式④是一個(gè)比較全面的方案,但是增加了學(xué)習(xí)成本、維護(hù)成本,因?yàn)樵黾恿讼⒅虚g件。
五、MySQL主從復(fù)制工作原理

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ù)器保持一致;
6、最后 I/O Thread 和 SQL Thread 將進(jìn)入睡眠狀態(tài),等待下一次被喚醒;
到此這篇關(guān)于MySQL數(shù)據(jù)庫(kù)和Redis緩存一致性的更新策略的文章就介紹到這了,更多相關(guān)MySQL和Redis緩存一致性?xún)?nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
mysql-5.5.28源碼安裝過(guò)程中錯(cuò)誤總結(jié)
介紹一下關(guān)于mysql-5.5.28源碼安裝過(guò)程中幾大錯(cuò)誤總結(jié),希望此文章對(duì)各位同學(xué)有所幫助。2013-10-10
MySQL觸發(fā)器之判斷更新操作前后數(shù)據(jù)是否改變
這篇文章主要介紹了MySQL觸發(fā)器之判斷更新操作前后數(shù)據(jù)是否改變方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2023-08-08
對(duì)MySQL子查詢(xún)的簡(jiǎn)單改寫(xiě)優(yōu)化
這篇文章主要介紹了對(duì)MySQL子查詢(xún)的簡(jiǎn)單改寫(xiě)優(yōu)化,文中的小修改主要將子查詢(xún)改為關(guān)聯(lián)從而降低查詢(xún)時(shí)關(guān)聯(lián)的次數(shù),需要的朋友可以參考下2015-05-05
解決Navicat for Mysql連接報(bào)錯(cuò)1251的問(wèn)題(連接失敗)
記得在之前給大家介紹過(guò)Navicat for Mysql連接報(bào)錯(cuò)的問(wèn)題,可能寫(xiě)的不夠詳細(xì),今天在稍作修改補(bǔ)充下,對(duì)Navicat for Mysql連接報(bào)錯(cuò)1251問(wèn)題感興趣的朋友跟隨小編一起看看吧2021-05-05
MySQL Event Scheduler(事件調(diào)度器)
事件調(diào)度器是在 MySQL 5.1 中新增的另一個(gè)特色功能,可以作為定時(shí)任務(wù)調(diào)度器,取代部分原先只能用操作系統(tǒng)任務(wù)調(diào)度器才能完成的定時(shí)功能。2010-06-06
Windows下MySQL8.0.11社區(qū)綠色版安裝步驟圖解
在本教程中使用MySQL最新的MySQL服務(wù)8.0.11的社區(qū)綠色版本進(jìn)行安裝,綠色版為zip格式的包,安裝步驟分為四大步驟,具體哪四大步驟大家跟隨腳本之家小編一起學(xué)習(xí)吧2018-05-05
完美轉(zhuǎn)換MySQL的字符集 解決查看utf8源文件中的亂碼問(wèn)題
本人轉(zhuǎn)換過(guò)好多數(shù)據(jù)了,也用過(guò)了好多的辦法,個(gè)人感覺(jué)最好用的就是使用MySQL命令導(dǎo)出導(dǎo)入中將字符集轉(zhuǎn)換過(guò)去2011-11-11
mysql配置SSL證書(shū)登錄的實(shí)現(xiàn)
應(yīng)國(guó)家等級(jí)保護(hù)三級(jí)安全要求,mysql 的 ssl 需要安全證書(shū)加密,本文主要介紹了mysql配置SSL證書(shū)登錄,感興趣的可以了解一下2021-09-09

