MySQL的緩存策略方式
一、MySQL 緩存方案用來(lái)干什么
用來(lái)緩存用戶定義的熱點(diǎn)數(shù)據(jù),用戶直接從緩存獲取熱點(diǎn)數(shù)據(jù),降低數(shù)據(jù)庫(kù)的讀寫壓力。
我們都知道內(nèi)存的讀取速度是磁盤訪問(wèn)速度的十萬(wàn)倍,那么這個(gè)緩存方案適用于什么場(chǎng)景呢?
1、既然讀取速度這么高,那么肯定就是讀的需求遠(yuǎn)大于寫的需求。
2、MySQL自身的緩沖層跟業(yè)務(wù)無(wú)關(guān)。MySQL中的緩存層也是用來(lái)緩存熱點(diǎn)數(shù)據(jù),但是這些數(shù)據(jù)包括索引、記錄等。mysql 緩沖層是從自身出發(fā),跟具體的業(yè)務(wù)無(wú)關(guān),而不是緩存我們用戶自己定義的熱點(diǎn)數(shù)據(jù)。
3、因此我們采用Redis作為我們的緩沖數(shù)據(jù)庫(kù),存放用戶定義的熱點(diǎn)數(shù)據(jù)。而MySQL只是作為項(xiàng)目的主要數(shù)據(jù)庫(kù),便于統(tǒng)計(jì)分析。

二、提升MySQL訪問(wèn)性能的方式
1、讀寫分離(MySQL的主從復(fù)制)
什么是讀寫分離?我們?cè)O(shè)置多個(gè)從數(shù)據(jù)庫(kù),從數(shù)據(jù)庫(kù)分布在多個(gè)不同的機(jī)器中,我們寫的操作依然是在主數(shù)據(jù)庫(kù),而讀的操作分給了從數(shù)據(jù)庫(kù),從數(shù)據(jù)庫(kù)的數(shù)據(jù)都是來(lái)自主數(shù)據(jù)庫(kù)。
這樣將讀寫進(jìn)行分離,會(huì)解決主數(shù)據(jù)庫(kù)讀的壓力。

我之前的文章講過(guò)主從之間如何進(jìn)行同步。我們得知主從之間可能會(huì)存在數(shù)據(jù)不一致的情況,但是最終的數(shù)據(jù)是一樣的,也就是可能會(huì)有延遲。比如博主寫了一篇文章并且剛剛發(fā)布,但是我的朋友說(shuō)還沒(méi)有看到,我說(shuō)你等個(gè)幾秒就好了。對(duì)于這種情況就很適合這種方案(讀取從數(shù)據(jù)庫(kù))。
但是對(duì)于讀的時(shí)效性很強(qiáng)的時(shí)候(一致性),我們就不可以讀取從數(shù)據(jù)庫(kù)了,而是直接讀取主數(shù)據(jù)庫(kù)。
2、連接池
在MySQL中存在連接池的組件,比如兩個(gè)客戶端連接上來(lái),但是我們使用多個(gè)線程去服務(wù)這幾個(gè)客戶端,可以大大提高并發(fā)訪問(wèn)數(shù)據(jù)庫(kù)的能力,并且,連接池的資源是可以復(fù)用的,我們可以避免連接建立或斷開(kāi),以及安全驗(yàn)證的開(kāi)銷。
他的網(wǎng)絡(luò)模型采用的并不是Reactor,而是select+阻塞io模型。對(duì)于上面所說(shuō)的多個(gè)線程服務(wù)客戶端,這里要特別注意,如果我們開(kāi)啟一個(gè)事務(wù)的話,一個(gè)事務(wù)內(nèi)不是包含很多SQL語(yǔ)句嘛,我們要保證這些事務(wù)語(yǔ)句全部在一個(gè)線程內(nèi)執(zhí)行。
3、異步連接
我們上面的連接池中講到了網(wǎng)絡(luò)模型采用的是 select + 非阻塞IO 。如果是阻塞IO的話也就是同步方式,我們發(fā)送多個(gè)SQL語(yǔ)句的話,他需要一個(gè)一個(gè)進(jìn)行執(zhí)行并且一個(gè)一個(gè)進(jìn)行返回。
但是我們采用異步的方式(非阻塞IO),我們發(fā)送多個(gè)命令,那么這些命令會(huì)異步執(zhí)行,誰(shuí)執(zhí)行完畢,誰(shuí)就通過(guò)回調(diào)函數(shù)進(jìn)行返回,大大節(jié)省網(wǎng)絡(luò)傳輸?shù)臅r(shí)間。
三、緩存方案是怎么解決的
1、緩存與MySQL一致性狀態(tài)分析
| 可行方案 | 不可行方案 |
| MySQL有,Redis無(wú) | MySQL無(wú),Redis有 |
| 都有,數(shù)據(jù)一致 | 都有,數(shù)據(jù)不一致 |
| 都沒(méi)有 |
我們根據(jù)上面所講述的主從復(fù)制,可以得知,MySQL中必須保存全部的數(shù)據(jù),而Redis從數(shù)據(jù)庫(kù)只是保存熱點(diǎn)數(shù)據(jù)。那么我們來(lái)討論上面這個(gè)表格的情況。
可行方案中:MySQL有,Redis無(wú)。MySQL沒(méi)有熱點(diǎn)數(shù)據(jù)需要進(jìn)行存儲(chǔ),所以Redis中沒(méi)有數(shù)據(jù),也就是說(shuō),用戶查找的這個(gè)數(shù)據(jù)并不是熱點(diǎn)數(shù)據(jù)。
- 第二種方案:都有,數(shù)據(jù)一致。MySQL中包含熱點(diǎn)數(shù)據(jù),而且Redis中也包含熱點(diǎn)數(shù)據(jù),并且是一致的數(shù)據(jù)。這樣我們查找從數(shù)據(jù)庫(kù)的時(shí)候,就不會(huì)出現(xiàn)與主數(shù)據(jù)庫(kù)查詢不同的情況了。
- 第三種:都沒(méi)有。也就是說(shuō)用戶查找的這個(gè)數(shù)據(jù)是不存在的,所以在MySQL中也不存在。
不可行方案:MySQL無(wú),Redis有。我們講的主從復(fù)制,需要保證寫數(shù)據(jù)是寫在主數(shù)據(jù)庫(kù)中的,所以主數(shù)據(jù)庫(kù)包含全部的信息,而從數(shù)據(jù)庫(kù)是從主數(shù)據(jù)庫(kù)中進(jìn)行同步的。所以這種情況是不允許的。
- 不可行方案:都有,數(shù)據(jù)不一致。我們講的主從復(fù)制中也說(shuō)到了從數(shù)據(jù)庫(kù)與主數(shù)據(jù)庫(kù)進(jìn)行同步可能會(huì)有延遲,但是最終數(shù)據(jù)是一致的。這樣就會(huì)導(dǎo)致我們可能讀取從數(shù)據(jù)庫(kù)的時(shí)候(讀取熱點(diǎn)數(shù)據(jù))可能會(huì)導(dǎo)致與主數(shù)據(jù)庫(kù)的數(shù)據(jù)不一致。
2、制定熱點(diǎn)數(shù)據(jù)的讀寫策略
對(duì)于讀的策略比較簡(jiǎn)單,由于是熱點(diǎn)數(shù)據(jù),我們直接讀取緩存(Redis),如果在Redis中找到了數(shù)據(jù),那就直接返回。如果未找到,那就讀取MySQL,如果在MySQL中讀到數(shù)據(jù),并且返回后,那就寫入Redis。這里寫入Redis中的是熱點(diǎn)數(shù)據(jù),并不是說(shuō),一查沒(méi)有,一查沒(méi)有,就全部寫入Redis中。
寫的策略分兩種,一種是安全為主,一種是效率為主。
如果要以安全為主,我們就要避免主數(shù)據(jù)庫(kù)和從數(shù)據(jù)庫(kù)讀取的數(shù)據(jù)不同的問(wèn)題。當(dāng)我們先寫入MySQL后,必然會(huì)出現(xiàn)MySQL與Redis數(shù)據(jù)不同的問(wèn)題,那么我們就不能先寫入MySQL。而是要先刪除Redis中的數(shù)據(jù),然后再寫入MySQL,最后將MySQL中的數(shù)據(jù)同步到Redis中去,這樣就保證兩方的數(shù)據(jù)一致了。但是我們的緩存方案就是為了提升效率,現(xiàn)在卻為了安全而降低了效率,這是我們不愿看到的。
如果要以效率為主,我們可以先寫入緩存,并且設(shè)置過(guò)期時(shí)間(大約是200毫秒),然后再寫入MySQL,當(dāng)寫入MySQL后,我們?cè)賹ySQL中的數(shù)據(jù)同步到Redis中去。當(dāng)同步到Redis中去的時(shí)候,這個(gè)過(guò)期時(shí)間也就到期了。過(guò)期時(shí)間是與MySQL網(wǎng)絡(luò)傳輸時(shí)間+MySQL處理時(shí)間+MySQL同步到Redis的時(shí)間。有個(gè)問(wèn)題是如果當(dāng)寫入MySQL寫入失敗,這個(gè)時(shí)候Redis中含有數(shù)據(jù),那么他就會(huì)提供臟數(shù)據(jù)。但是這個(gè)問(wèn)題也就200毫秒的存活時(shí)間,因?yàn)閺臄?shù)據(jù)庫(kù)會(huì)找主數(shù)據(jù)庫(kù)進(jìn)行同步。
四、緩存方案問(wèn)題的解決方法
1、緩存穿透
問(wèn)題:如果黑客讓客戶端一直讀取MySQL和Redis中都不存在的數(shù)據(jù),那么所有的讀取操作都落在了MySQL中,那么就會(huì)造成MySQL中訪問(wèn)的性能急劇降低。
解決:如果在Redis和MySQL中讀取的數(shù)據(jù)都不存在,那么就在Redis中設(shè)置一個(gè)<Key,nil>,代表查找的這個(gè)熱點(diǎn)數(shù)據(jù)不存在?;蛘卟渴?strong>布隆過(guò)濾器(類似于哈希表),使這些數(shù)據(jù)只能增加,不能刪除,具體可以搜一搜。
2、緩存擊穿
問(wèn)題:如果Redis中沒(méi)有,但是MySQL中有,也就是說(shuō)本來(lái)一個(gè)熱點(diǎn)數(shù)據(jù),在Redis中存在,但是過(guò)期了,那么大量的并發(fā)請(qǐng)求讀取操作就會(huì)落到MySQL中,這樣就造成MySQL訪問(wèn)的性能急劇降低。
解決:我們可以將過(guò)熱的數(shù)據(jù)設(shè)置成不過(guò)期的狀態(tài)?;蛘呤翘砑?strong>分布式鎖,將并發(fā)的請(qǐng)求操作,變成串行執(zhí)行。
3、緩存雪崩
問(wèn)題:我們?cè)趯懭隦edis中的數(shù)據(jù)是需要加入過(guò)期時(shí)間的,但是當(dāng)我們不小心將多個(gè)過(guò)熱數(shù)據(jù)的過(guò)期時(shí)間設(shè)置成統(tǒng)一時(shí)間,就會(huì)面臨大量熱點(diǎn)數(shù)據(jù)集中失效的問(wèn)題,雖然失效,但是在MySQL中還是存在這個(gè)數(shù)據(jù),所以大量的請(qǐng)求讀取操作就會(huì)落到MySQL中去,就會(huì)造成MySQL訪問(wèn)性能急劇降低。
解決:我們可以將這個(gè)過(guò)期時(shí)間給錯(cuò)開(kāi),避免同時(shí)過(guò)期。當(dāng)然我們可以在重啟MySQL的時(shí)候,先將一些熱數(shù)據(jù)先緩存到Redis中。
五、緩存方案的弊端
我們上面講到一個(gè)問(wèn)題就是,不能支持多語(yǔ)句的事務(wù),如果要支持的話,需要保證begin到commit之間的全部語(yǔ)句都在一條線程中執(zhí)行。而且Redis不支持回滾,并且有時(shí)候會(huì)造成Redis與MySQL數(shù)據(jù)不一致。
總結(jié)
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
MySQL從一個(gè)表中查出數(shù)據(jù)并插入到另一個(gè)表的詳細(xì)處理方案
這篇文章主要給大家介紹了關(guān)于MySQL從一個(gè)表中查出數(shù)據(jù)并插入到另一個(gè)表的詳細(xì)處理方案,文中通過(guò)圖文介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用MySQL具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2023-12-12
在Windows環(huán)境下安裝MySQL 的教程圖解
這篇文章主要介紹了在Windows環(huán)境下安裝MySQL 的教程圖解,本文圖文并茂給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07
MyBatis中實(shí)現(xiàn)動(dòng)態(tài)SQL標(biāo)簽
動(dòng)態(tài)SQL是MyBatis的一項(xiàng)強(qiáng)大功能,它允許開(kāi)發(fā)者根據(jù)條件動(dòng)態(tài)地生成SQL語(yǔ)句,本文主要介紹了MyBatis中實(shí)現(xiàn)動(dòng)態(tài)SQL標(biāo)簽,感興趣的可以可以了解一下2024-09-09
Windows7下安裝使用MySQL8.0.16修改密碼、連接Navicat問(wèn)題
這篇文章主要介紹了Windows7下安裝使用MySQL8.0.16修改密碼、連接Navicat問(wèn)題,本文給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-06-06
安裝MySQL 5后無(wú)法啟動(dòng)(不能Start service)解決方法小結(jié)
有時(shí)候我們?cè)诎惭bmysql軟件時(shí),卻無(wú)法啟動(dòng),或服務(wù)器安全設(shè)置以后都可能導(dǎo)致mysql無(wú)法運(yùn)行2012-07-07
Last_Errno:?1062,Last_Error:?Error?Duplicate?entry
Last_Errno:?1062,Last_Error:?Error?Duplicate?entry?...?for?key?PRIMARY2014-02-02

