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

1、當 master 主服務器上的數據發(fā)生改變時,則將其改變寫入二進制事件日志文件中;
2、salve 從服務器會在一定時間間隔內對 master 主服務器上的二進制日志進行探測,探測其是否發(fā)生過改變,
如果探測到 master 主服務器的二進制事件日志發(fā)生了改變,則開始一個 I/O Thread 請求 master 二進制事件日志;
3、同時 master 主服務器為每個 I/O Thread 啟動一個dump Thread,用于向其發(fā)送二進制事件日志;
4、slave 從服務器將接收到的二進制事件日志保存至自己本地的中繼日志文件中;
5、salve 從服務器將啟動 SQL Thread 從中繼日志中讀取二進制日志,在本地重放,使得其數據和主服務器保持一致;
6、最后 I/O Thread 和 SQL Thread 將進入睡眠狀態(tài),等待下一次被喚醒;
到此這篇關于MySQL數據庫和Redis緩存一致性的更新策略的文章就介紹到這了,更多相關MySQL和Redis緩存一致性內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
解決Navicat for Mysql連接報錯1251的問題(連接失敗)
記得在之前給大家介紹過Navicat for Mysql連接報錯的問題,可能寫的不夠詳細,今天在稍作修改補充下,對Navicat for Mysql連接報錯1251問題感興趣的朋友跟隨小編一起看看吧2021-05-05
Windows下MySQL8.0.11社區(qū)綠色版安裝步驟圖解
在本教程中使用MySQL最新的MySQL服務8.0.11的社區(qū)綠色版本進行安裝,綠色版為zip格式的包,安裝步驟分為四大步驟,具體哪四大步驟大家跟隨腳本之家小編一起學習吧2018-05-05
完美轉換MySQL的字符集 解決查看utf8源文件中的亂碼問題
本人轉換過好多數據了,也用過了好多的辦法,個人感覺最好用的就是使用MySQL命令導出導入中將字符集轉換過去2011-11-11

