淺談MySQL數(shù)據(jù)同步到 Redis 緩存的幾種方法
1 Mysql查完數(shù)據(jù),再同步寫入到Redis中
缺點1:會對接口造成延遲,因為同步寫入redis本身就有延遲,并且還要做重試,如果redis寫入失敗,還需要重試,那就更費時間了。
缺點2:不解耦,如果redis崩了,那直接卡線程了
缺點3:如果人為該數(shù)據(jù)庫,那就沒法同步了, 除非再人為刪除對應的Redis,但刪除Redis這個過程也有個時間差
2 Mysql查完數(shù)據(jù),通過發(fā)送MQ,在消費者線程去同步Redis
缺點1:多了層MQ,也就是會有很大的概率導致同步延遲問題.
缺點2:要對MQ的可用性做預防
缺點3:如果人為該數(shù)據(jù)庫,那就沒法同步了
優(yōu)點1:可以大幅減少接口的延遲返回的問題
優(yōu)點2:MQ本身有重試機制,無需人工去寫重試代碼
優(yōu)點3:解耦,把查詢Mysql和同步Redis完全分離,互不干擾
3 訂閱Mysql的Binlog文件(可借助Canal來進行)
CanalServer會偽裝成MysqlServer從庫,去訂閱MysqlServer主庫的Binlog文件
Canal啟動的時候會配置對應的消息MQ(RabbitMQ, RocketMQ, Kafka), 監(jiān)聽到Binlog文件有變化是,會把變化的sql語句轉換成json格式,并作為消息內容發(fā)送到MQ中
項目中只要監(jiān)聽對應MQ,就能拿到Binlog改動的內容,Json數(shù)據(jù)中有明確的操作類型(CURD), 以及對應的數(shù)據(jù)。把對應數(shù)據(jù)同步到redis即可
缺點1:canal訂閱Binlog的整個操作過程是單線程的,所以面臨超高并發(fā)的情況下,性能可能不太出色。當然可以部署多個Canal 與 多個消費者,但是要注意消息重復消費問題,做好冪等性校驗
優(yōu)點1:即使人為改數(shù)據(jù)庫,也會監(jiān)聽到,并且也會同步
優(yōu)點2:異步同步,不會對接口返回有格外延遲
4 延遲雙刪
在執(zhí)行修改sql之前,先將redis的數(shù)據(jù)刪除
執(zhí)行更新sql
延遲一段時間
再次刪除redis的數(shù)據(jù)
// 延遲雙刪偽代碼 deleteRedisCache(key); // 刪除redis緩存 updateMysqlSql(obj); // 更新mysql Thread.sleep(100); // 延遲一段時間 deleteRedisCache(key); // 再次刪除該key的緩存
缺點:這個延遲時間不好把控,到底延遲多久,這個很難去評估
擴展: 如果不使用延遲雙刪,僅僅是delete緩存,然后改mysql數(shù)據(jù)。只有這兩步會出現(xiàn)什么問題呢?
5. 單個請求,單線程沒問題,高并發(fā)多線程下會出問題
6. 如果Thread1線程要更新數(shù)據(jù),此時Thread1線程把redis清理了
7. 此時Thread2線程來了,但Thread1還沒有更新mysql完畢
8. Thread2查詢redis肯定是null,此時Thread2就要查mysql了,然后再把查到的數(shù)據(jù)寫到緩存
9. 由于Thread1還沒來得及修改mysql數(shù)據(jù),所以此時Thread2查出來的數(shù)據(jù)是【舊數(shù)據(jù)】,Thread2把舊數(shù)據(jù)又寫入Redis 了
10. 此時Thread3線程來了,查詢Redis發(fā)現(xiàn)有數(shù)據(jù),則直接拿緩存數(shù)據(jù)了,此時【Thread3查出來的是舊數(shù)據(jù)】,直接帶著舊數(shù)據(jù)返回了,這就是問題所在
11. 而延遲雙刪的第二次刪除作用就是防止Thread2把舊數(shù)據(jù)又寫入了,有了延遲雙刪,Thread3查詢Redis的時候還是null,就會從mysql 去拿最新數(shù)據(jù)了
12. 所以正常的這個延遲時間,應該是Thread2查緩存到拿mysql數(shù)據(jù),到再保存到redis這整個時間,作為Thread1的延遲時間,但是這個Thread2這個過程的時間會受到很多因素影響,因此很難斷定究竟會是多久
5 延遲雙寫
// 延遲雙寫偽代碼 updateMysqlSql(obj); // 更新mysql addRedis(key); // 再次刪除該key的緩存
上述代碼缺陷;
- 高并發(fā)下,兩條線程同時執(zhí)行上面代碼,并對mysql 修改,且修改內容不通,可能會導致Redis與Mysql數(shù)據(jù)不一致
- T1線程執(zhí)行完updateMysqlSql,釋放了行鎖,此時T2線程再執(zhí)行updateMysqlSql 與 addRedis, 最后T1執(zhí)行addRedis,這種情況會導致數(shù)據(jù)庫改成了T2線程的數(shù)據(jù),但Redis卻是T1線程的數(shù)據(jù)
優(yōu)化
// 完美延遲雙寫偽代碼 開啟事務 updateMysqlSql(obj); // 更新mysql addRedis(key); // 再次刪除該key的緩存 提交事務
上述代碼改正:
把兩句代碼放到一個事務里面,只有T1執(zhí)行完Mysql 與 Redis的時候,T2才能開始執(zhí)行,就可以保證數(shù)據(jù)一致性。推薦使用分布式鎖
雙寫缺點:Mysql 與 Redis是單線程的。性能方面不行,因此不推薦使用
6 總結
推薦使用Canal的方式,進行異步同步。其次是MQ方式
到此這篇關于淺談MySQL數(shù)據(jù)同步到 Redis 緩存的幾種方法的文章就介紹到這了,更多相關MySQL數(shù)據(jù)同步到Redis緩存內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
MySQL thread_stack連接線程的優(yōu)化
當有新的連接請求時,MySQL首先會檢查Thread Cache中是否存在空閑連接線程,如果存在則取出來直接使用,如果沒有空閑連接線程,才創(chuàng)建新的連接線程2017-04-04MySQL too many connections錯誤的原因及解決
這篇文章主要介紹了MySQL too many connections錯誤的原因及解決,幫助大家更好的理解和學習使用MySQL,感興趣的朋友可以了解下2021-03-03MySQL運行報錯:“Expression?#1?of?SELECT?list?is?not?in?GR
這篇文章主要給大家介紹了關于MySQL運行報錯:“Expression?#1?of?SELECT?list?is?not?in?GROUP?BY?clause?and?contains?nonaggre”的解決方法,文中將解決方法介紹的非常詳細,需要的朋友可以參考下2022-06-06mysql中key 、primary key 、unique key 與index區(qū)別
這篇文章主要介紹了mysql中key 、primary key 、unique key 與index區(qū)別的相關資料,需要的朋友可以參考下2016-10-10