我使用緩存,踩過的7個坑
前言
緩存在我們?nèi)粘9ぷ髦校?jīng)常會使用,但如果用不好坑也挺多的。
這篇文章總結(jié)了我工作中使用緩存遇到過的7個坑,還是非常有參考價值得,希望對你會有所幫助。
圖片
1 緩存穿透
大部分情況下,加緩存的目的是:為了減輕數(shù)據(jù)庫的壓力,提升系統(tǒng)的性能。
一般情況下,如果有用戶請求過來,先查緩存,如果緩存中存在數(shù)據(jù),則直接返回。
如果緩存中不存在,則再查數(shù)據(jù)庫,如果數(shù)據(jù)庫中存在,則將數(shù)據(jù)放入緩存,然后返回。如果數(shù)據(jù)庫中也不存在,則直接返回失敗。
流程圖如下:
圖片
但如果出現(xiàn)以下這兩種特殊情況,比如:
- 用戶請求的id在緩存中不存在。
- 惡意用戶偽造不存在的id發(fā)起請求。
這樣的用戶請求導(dǎo)致的結(jié)果是:每次從緩存中都查不到數(shù)據(jù),而需要查詢數(shù)據(jù)庫,同時數(shù)據(jù)庫中也沒有查到該數(shù)據(jù),也沒法放入緩存。
也就是說,每次這個用戶請求過來的時候,都要查詢一次數(shù)據(jù)庫。
圖片
圖中標紅的箭頭表示每次走的路線。
很顯然,緩存根本沒起作用,好像被穿透了一樣,每次都會去訪問數(shù)據(jù)庫。
這就是我們所說的:緩存穿透問題。
如果此時穿透了緩存,而直接數(shù)據(jù)庫的請求數(shù)量非常多,數(shù)據(jù)庫可能因為扛不住壓力而掛掉。嗚嗚嗚。
那么問題來了,如何解決這個問題呢?
1.1 校驗參數(shù)
我們可以對用戶id做檢驗。
比如你的合法id是15xxxxxx,以15開頭的。如果用戶傳入了16開頭的id,比如:16232323,則參數(shù)校驗失敗,直接把相關(guān)請求攔截掉。這樣可以過濾掉一部分惡意偽造的用戶id。
1.2 使用布隆過濾器
如果數(shù)據(jù)比較少,我們可以把數(shù)據(jù)庫中的數(shù)據(jù),全部放到內(nèi)存的一個map中。
這樣能夠非常快速的識別,數(shù)據(jù)在緩存中是否存在。如果存在,則讓其訪問緩存。如果不存在,則直接拒絕該請求。
但如果數(shù)據(jù)量太多了,有數(shù)千萬或者上億的數(shù)據(jù),全都放到內(nèi)存中,很顯然會占用太多的內(nèi)存空間。
那么,有沒有辦法減少內(nèi)存空間呢?
答:這就需要使用布隆過濾器了。
布隆過濾器底層使用bit數(shù)組存儲數(shù)據(jù),該數(shù)組中的元素默認值是0。
布隆過濾器第一次初始化的時候,會把數(shù)據(jù)庫中所有已存在的key,經(jīng)過一些列的hash算法(比如:三次hash算法)計算,每個key都會計算出多個位置,然后把這些位置上的元素值設(shè)置成1。
圖片
之后,有用戶key請求過來的時候,再用相同的hash算法計算位置。
- 如果多個位置中的元素值都是1,則說明該key在數(shù)據(jù)庫中已存在。這時允許繼續(xù)往后面操作。
- 如果有1個以上的位置上的元素值是0,則說明該key在數(shù)據(jù)庫中不存在。這時可以拒絕該請求,而直接返回。
1.3 緩存空值
上面使用布隆過濾器,雖說可以過濾掉很多不存在的用戶id請求。但它除了增加系統(tǒng)的復(fù)雜度之外,會帶來兩個問題:
- 布隆過濾器存在誤殺的情況,可能會把少部分正常用戶的請求也過濾了。
- 如果用戶信息有變化,需要實時同步到布隆過濾器,不然會有問題。
所以,通常情況下,我們很少用布隆過濾器解決緩存穿透問題。其實,還有另外一種更簡單的方案,即:緩存空值。
當某個用戶id在緩存中查不到,在數(shù)據(jù)庫中也查不到時,也需要將該用戶id緩存起來,只不過值是空的。
這樣后面的請求,再拿相同的用戶id發(fā)起請求時,就能從緩存中獲取空數(shù)據(jù),直接返回了,而無需再去查一次數(shù)據(jù)庫。
優(yōu)化之后的流程圖如下:
圖片
關(guān)鍵點是不管從數(shù)據(jù)庫有沒有查到數(shù)據(jù),都將結(jié)果放入緩存中,只是如果沒有查到數(shù)據(jù),緩存中的值是空的罷了。
2 緩存擊穿
有時候,我們在訪問熱點數(shù)據(jù)時。比如:我們在某個商城購買某個熱門商品。
為了保證訪問速度,通常情況下,商城系統(tǒng)會把商品信息放到緩存中。但如果某個時刻,該商品到了過期時間失效了。
此時,如果有大量的用戶請求同一個商品,但該商品在緩存中失效了,一下子這些用戶請求都直接懟到數(shù)據(jù)庫,可能會造成瞬間數(shù)據(jù)庫壓力過大,而直接掛掉。
流程圖如下:
圖片
那么,如何解決這個問題呢?
2.1 加鎖
數(shù)據(jù)庫壓力過大的根源是,因為同一時刻太多的請求訪問了數(shù)據(jù)庫。
如果我們能夠限制,同一時刻只有一個請求才能訪問某個productId的數(shù)據(jù)庫商品信息,不就能解決問題了?
答:沒錯,我們可以用加鎖的方式,實現(xiàn)上面的功能。
偽代碼如下:
try {
String result = jedis.set(productId, requestId, "NX", "PX", expireTime);
if ("OK".equals(result)) {
return queryProductFromDbById(productId);
}
} finally{
unlock(productId,requestId);
}
return null;
在訪問數(shù)據(jù)庫時加鎖,防止多個相同productId的請求同時訪問數(shù)據(jù)庫。
然后,還需要一段代碼,把從數(shù)據(jù)庫中查詢到的結(jié)果,又重新放入緩存中。辦法挺多的,在這里我就不展開了。
2.2 自動續(xù)期
出現(xiàn)緩存擊穿問題是由于key過期了導(dǎo)致的。那么,我們換一種思路,在key快要過期之前,就自動給它續(xù)期,不就OK了?
答:沒錯,我們可以用job給指定key自動續(xù)期。
比如說,我們有個分類功能,設(shè)置的緩存過期時間是30分鐘。但有個job每隔20分鐘執(zhí)行一次,自動更新緩存,重新設(shè)置過期時間為30分鐘。
圖片
這樣就能保證,分類緩存不會失效。
2.3 永久有效
此外,對于很多熱門key,其實是可以不用設(shè)置過期時間,讓其永久有效的。
比如參與秒殺活動的熱門商品,由于這類商品id并不多,在緩存中我們可以不設(shè)置過期時間。
在秒殺活動開始前,我們先用一個程序提前從數(shù)據(jù)庫中查詢出商品的數(shù)據(jù),然后同步到緩存中,提前做預(yù)熱。
等秒殺活動結(jié)束一段時間之后,我們再手動刪除這些無用的緩存即可。
3 緩存雪崩
而緩存雪崩是緩存擊穿的升級版,緩存擊穿說的是某一個熱門key失效了,而緩存雪崩說的是有多個熱門key同時失效。
看起來,如果發(fā)生緩存雪崩,問題更嚴重。
緩存雪崩目前有兩種:
- 有大量的熱門緩存,同時失效。會導(dǎo)致大量的請求,訪問數(shù)據(jù)庫。而數(shù)據(jù)庫很有可能因為扛不住壓力,而直接掛掉。
- 緩存服務(wù)器down機了,可能是機器硬件問題,或者機房網(wǎng)絡(luò)問題。總之,造成了整個緩存的不可用。
歸根結(jié)底都是有大量的請求,透過緩存,而直接訪問數(shù)據(jù)庫了。
圖片
那么,要如何解決這個問題呢?
3.1 過期時間加隨機數(shù)
為了解決緩存雪崩問題,我們首先要盡量避免緩存同時失效的情況發(fā)生。
這就要求我們不要設(shè)置相同的過期時間。
可以在設(shè)置的過期時間基礎(chǔ)上,再加個1~60秒的隨機數(shù)。
實際過期時間 = 過期時間 + 1~60秒的隨機數(shù)
這樣即使在高并發(fā)的情況下,多個請求同時設(shè)置過期時間,由于有隨機數(shù)的存在,也不會出現(xiàn)太多相同的過期key。
3.2 保證高可用
針對緩存服務(wù)器down機的情況,在前期做系統(tǒng)設(shè)計時,可以做一些高可用架構(gòu)。
比如:如果使用了redis,可以使用哨兵模式,或者集群模式,避免出現(xiàn)單節(jié)點故障導(dǎo)致整個redis服務(wù)不可用的情況。
使用哨兵模式之后,當某個master服務(wù)下線時,自動將該master下的某個slave服務(wù)升級為master服務(wù),替代已下線的master服務(wù)繼續(xù)處理請求。
3.3 服務(wù)降級
如果做了高可用架構(gòu),redis服務(wù)還是掛了,該怎么辦呢?
這時候,就需要做服務(wù)降級了。
我們需要配置一些默認的兜底數(shù)據(jù)。
程序中有個全局開關(guān),比如有10個請求在最近一分鐘內(nèi),從redis中獲取數(shù)據(jù)失敗,則全局開關(guān)打開。后面的新請求,就直接從配置中心中獲取默認的數(shù)據(jù)。
當然,還需要有個job,每隔一定時間去從redis中獲取數(shù)據(jù),如果在最近一分鐘內(nèi)可以獲取到兩次數(shù)據(jù)(這個參數(shù)可以自己定),則把全局開關(guān)關(guān)閉。后面來的請求,又可以正常從redis中獲取數(shù)據(jù)了。
需要特別說一句,該方案并非所有的場景都適用,需要根據(jù)實際業(yè)務(wù)場景決定。
4 數(shù)據(jù)不一致
數(shù)據(jù)庫和緩存(比如:redis)雙寫數(shù)據(jù)一致性問題,是一個跟開發(fā)語言無關(guān)的公共問題。尤其在高并發(fā)的場景下,這個問題變得更加嚴重。
那么,我們該如何更新緩存呢?
目前有以下4種方案:
- 先寫緩存,再寫數(shù)據(jù)庫
- 先寫數(shù)據(jù)庫,再寫緩存
- 先刪緩存,再寫數(shù)據(jù)庫
- 先寫數(shù)據(jù)庫,再刪緩存
4.1 先寫緩存,再寫數(shù)據(jù)庫
對于更新緩存的方案,很多人第一個想到的可能是在寫操作中直接更新緩存(寫緩存),更直接明了。
那么,問題來了:在寫操作中,到底是先寫緩存,還是先寫數(shù)據(jù)庫呢?
我們在這里先聊聊先寫緩存,再寫數(shù)據(jù)庫的情況,因為它的問題最嚴重。
某一個用戶的每一次寫操作,如果剛寫完緩存,突然網(wǎng)絡(luò)出現(xiàn)了異常,導(dǎo)致寫數(shù)據(jù)庫失敗了。
圖片
其結(jié)果是緩存更新成了最新數(shù)據(jù),但數(shù)據(jù)庫沒有,這樣緩存中的數(shù)據(jù)不就變成臟數(shù)據(jù)了?如果此時該用戶的查詢請求,正好讀取到該數(shù)據(jù),就會出現(xiàn)問題,因為該數(shù)據(jù)在數(shù)據(jù)庫中根本不存在,這個問題非常嚴重。
我們都知道,緩存的主要目的是把數(shù)據(jù)庫的數(shù)據(jù)臨時保存在內(nèi)存,便于后續(xù)的查詢,提升查詢速度。
但如果某條數(shù)據(jù),在數(shù)據(jù)庫中都不存在,你緩存這種“假數(shù)據(jù)”又有啥意義呢?
因此,先寫緩存,再寫數(shù)據(jù)庫的方案是不可取的,在實際工作中用得不多。
4.2 先寫數(shù)據(jù)庫,再寫緩存
既然上面的方案行不通,接下來,聊聊先寫數(shù)據(jù)庫,再寫緩存的方案,該方案在低并發(fā)編程中有人在用(我猜的)。
用戶的寫操作,先寫數(shù)據(jù)庫,再寫緩存,可以避免之前“假數(shù)據(jù)”的問題。但它卻帶來了新的問題。
什么問題呢?
4.2.1 寫緩存失敗了
如果把寫數(shù)據(jù)庫和寫緩存操作,放在同一個事務(wù)當中,當寫緩存失敗了,我們可以把寫入數(shù)據(jù)庫的數(shù)據(jù)進行回滾。
圖片
如果是并發(fā)量比較小,對接口性能要求不太高的系統(tǒng),可以這么玩。
但如果在高并發(fā)的業(yè)務(wù)場景中,寫數(shù)據(jù)庫和寫緩存,都屬于遠程操作。為了防止出現(xiàn)大事務(wù),造成的死鎖問題,通常建議寫數(shù)據(jù)庫和寫緩存不要放在同一個事務(wù)中。
也就是說在該方案中,如果寫數(shù)據(jù)庫成功了,但寫緩存失敗了,數(shù)據(jù)庫中已寫入的數(shù)據(jù)不會回滾。
這就會出現(xiàn):數(shù)據(jù)庫是新數(shù)據(jù),而緩存是舊數(shù)據(jù),兩邊數(shù)據(jù)不一致的情況。
4.2.2 高并發(fā)下的問題
假設(shè)在高并發(fā)的場景中,針對同一個用戶的同一條數(shù)據(jù),有兩個寫數(shù)據(jù)請求:a和b,它們同時請求到業(yè)務(wù)系統(tǒng)。
在這個過程當中,可能會出現(xiàn)請求b在緩存中的新數(shù)據(jù),被請求a的舊數(shù)據(jù)覆蓋了。
也就是說:在高并發(fā)場景中,如果多個線程同時執(zhí)行先寫數(shù)據(jù)庫,再寫緩存的操作,可能會出現(xiàn)數(shù)據(jù)庫是新值,而緩存中是舊值,兩邊數(shù)據(jù)不一致的情況。
4.2.3 浪費系統(tǒng)資源
該方案還有一個比較大的問題就是:每個寫操作,寫完數(shù)據(jù)庫,會馬上寫緩存,比較浪費系統(tǒng)資源。
為什么這么說呢?
你可以試想一下,如果寫的緩存,并不是簡單的數(shù)據(jù)內(nèi)容,而是要經(jīng)過非常復(fù)雜的計算得出的最終結(jié)果。這樣每寫一次緩存,都需要經(jīng)過一次非常復(fù)雜的計算,不是非常浪費系統(tǒng)資源嗎?
尤其是cpu和內(nèi)存資源。
還有些業(yè)務(wù)場景比較特殊:寫多讀少。
如果在這類業(yè)務(wù)場景中,每個用的寫操作,都需要寫一次緩存,有點得不償失。
由此可見,在高并發(fā)的場景中,先寫數(shù)據(jù)庫,再寫緩存,這套方案問題挺多的,也不太建議使用。
4.3 先刪緩存,再寫數(shù)據(jù)庫
說白了,在用戶的寫操作中,先執(zhí)行刪除緩存操作,再去寫數(shù)據(jù)庫。這套方案,可以是可以,但也會有一樣問題。
4.3.1 高并發(fā)下的問題
假設(shè)在高并發(fā)的場景中,同一個用戶的同一條數(shù)據(jù),有一個讀數(shù)據(jù)請求c,還有另一個寫數(shù)據(jù)請求d(一個更新操作),同時請求到業(yè)務(wù)系統(tǒng)。
在這個過程當中,有可能會出現(xiàn)請求d的新值,并沒有被請求c寫入緩存,同樣會導(dǎo)致緩存和數(shù)據(jù)庫的數(shù)據(jù)不一致的情況。
4.4 先寫數(shù)據(jù)庫,再刪緩存
在高并發(fā)的場景中,有一個讀數(shù)據(jù)請求f,有一個寫數(shù)據(jù)請求e。
在高并發(fā)的場景中,有一個讀數(shù)據(jù)請求,有一個寫數(shù)據(jù)請求,更新過程如下:
請求e先寫數(shù)據(jù)庫,由于網(wǎng)絡(luò)原因卡頓了一下,沒有來得及刪除緩存。請求f查詢緩存,發(fā)現(xiàn)緩存中有數(shù)據(jù),直接返回該數(shù)據(jù)。請求e刪除緩存。在這個過程中,只有請求f讀了一次舊數(shù)據(jù),后來舊數(shù)據(jù)被請求e及時刪除了,看起來問題不大。
但如果是讀數(shù)據(jù)請求先過來呢?
- 請求f查詢緩存,發(fā)現(xiàn)緩存中有數(shù)據(jù),直接返回該數(shù)據(jù)。
- 請求e先寫數(shù)據(jù)庫。
- 請求e刪除緩存。
這種情況看起來也沒問題呀?
答:對的。
但就怕出現(xiàn)下面這種情況,即緩存自己失效了。如下圖所示:
圖片
- 緩存過期時間到了,自動失效。
- 請求f查詢緩存,發(fā)緩存中沒有數(shù)據(jù),查詢數(shù)據(jù)庫的舊值,但由于網(wǎng)絡(luò)原因卡頓了,沒有來得及更新緩存。
- 請求e先寫數(shù)據(jù)庫,接著刪除了緩存。
- 請求f更新舊值到緩存中。
這時,緩存和數(shù)據(jù)庫的數(shù)據(jù)同樣出現(xiàn)不一致的情況了。
但這種情況還是比較少的,需要同時滿足以下條件才可以:
- 緩存剛好自動失效。
- 請求f從數(shù)據(jù)庫查出舊值,更新緩存的耗時,比請求e寫數(shù)據(jù)庫,并且刪除緩存的還長。
我們都知道查詢數(shù)據(jù)庫的速度,一般比寫數(shù)據(jù)庫要快,更何況寫完數(shù)據(jù)庫,還要刪除緩存。所以絕大多數(shù)情況下,寫數(shù)據(jù)請求比讀數(shù)據(jù)情況耗時更長。
由此可見,系統(tǒng)同時滿足上述兩個條件的概率非常小。
如果大家想更詳細的了解數(shù)據(jù)和緩存雙寫一致性問題,可以看看我之前寫的一篇文章《如何保證數(shù)據(jù)庫和緩存雙寫一致性?》,里面有非常詳細的介紹。
5 大key問題
我們在使用緩存的時候,特別是Redis,還有一個經(jīng)常會遇到的問題是大key問題。
可能系統(tǒng)剛上線時,數(shù)據(jù)量少,在Redis中定義的key比較小,開發(fā)人員在做系統(tǒng)設(shè)計時,也沒考慮這個問題。
系統(tǒng)運行了很長一段時間也沒有問題。
但隨著時間的推移,用戶的數(shù)據(jù)越來越多,慢慢形成了大key問題。
可能在突然的某一天之后發(fā)現(xiàn),線上某個接口耗時越來越長了。
追查原因,發(fā)現(xiàn)是大key問題導(dǎo)致的。
大key問題是指:緩存中單個key的value值過大。
之前我開發(fā)過一個分類樹查詢接口,為了性能考慮,使用job提前將分類樹,保存到緩存中。
剛開始分類不多,只有幾百個,分類樹查詢接口的響應(yīng)挺快的。
但用了幾年之后,分類數(shù)據(jù)漲到了上萬個,該接口出現(xiàn)了性能問題,一查發(fā)現(xiàn)是大key引起的。
我們需要做優(yōu)化,那么如何優(yōu)化呢?
5.1 縮減字段名
為了優(yōu)化在Redis中存儲數(shù)據(jù)的大小,我們首先需要對數(shù)據(jù)進行瘦身。
只保存需要用到的字段。
例如:
@AllArgsConstructor
@Data
public class Category {
private Long id;
private String name;
private Long parentId;
private Date inDate;
private Long inUserId;
private String inUserName;
private List<Category> children;
}
像這個分類對象中inDate、inUserId和inUserName字段是可以不用保存的。
修改自動名稱。
例如:
@AllArgsConstructor
@Data
public class Category {
/**
* 分類編號
*/
@JsonProperty("i")
private Long id;
/**
* 分類層級
*/
@JsonProperty("l")
private Integer level;
/**
* 分類名稱
*/
@JsonProperty("n")
private String name;
/**
* 父分類編號
*/
@JsonProperty("p")
private Long parentId;
/**
* 子分類列表
*/
@JsonProperty("c")
private List<Category> children;
}
由于在一萬多條數(shù)據(jù)中,每條數(shù)據(jù)的字段名稱是固定的,他們的重復(fù)率太高了。
由此,可以在json序列化時,改成一個簡短的名稱,以便于返回更少的數(shù)據(jù)大小。
5.2 數(shù)據(jù)做壓縮
這還不夠,需要對存儲的數(shù)據(jù)做壓縮。
之前在Redis中保存的key/value,其中的value是json格式的字符串。
其實RedisTemplate支持,value保存byte數(shù)組。
先將json字符串數(shù)據(jù)用GZip工具類壓縮成byte數(shù)組,然后保存到Redis中。
再獲取數(shù)據(jù)時,將byte數(shù)組轉(zhuǎn)換成json字符串,然后再轉(zhuǎn)換成分類樹。
這樣優(yōu)化之后,保存到Redis中的分類樹的數(shù)據(jù)大小,一下子減少了10倍,Redis的大key問題被解決了。
如果大家對大key問題如何優(yōu)化,比較感興趣,可以看看我的另一篇文章《分類樹,我從2s優(yōu)化到0.1s》,里面有真實的案例。
6 熱key問題
不知道大家聽說過二八原理沒有。
80%的用戶經(jīng)常訪問20%的熱點數(shù)據(jù)。
這樣帶來的結(jié)果是數(shù)據(jù)的傾斜,不能均勻分布,尤其是高并發(fā)系統(tǒng)中問題比較大。
比如你現(xiàn)在搞了一個促銷活動,有幾款商品性價比非常高,這些商品數(shù)據(jù)在Redis中按分片保存的,不同的數(shù)據(jù)保存在不同的服務(wù)器節(jié)點上。
如果用戶瘋狂搶購其中3款商品,而這3款商品正好保存在同一臺Redis服務(wù)端節(jié)點。
這樣會出現(xiàn)大量的用戶請求集中訪問同一天Redis服務(wù)器節(jié)點,該節(jié)點很有可能會因為扛不住這么大的壓力,而直接down機。
這個就是熱key問題帶來的危害。
那么,如何解決這個問題呢?
6.1 拆分key
在促銷活動開始之前,我們要提前做好評估,分析這些商品哪些是熱點商品。
然后將熱點商品分開保存,不要集中保存到同一臺Redis服務(wù)器節(jié)點。
這樣不同的Redis服務(wù)器節(jié)點,可以分攤一些用戶的請求壓力。
6.2 增加本地緩存
對應(yīng)熱key,我們可以增加一層本地緩存,能夠提升性能的同時也能避免Redis訪問量過大的問題。
但帶來的壞處是,可能會出現(xiàn)數(shù)據(jù)不一致問題,要根據(jù)實際的業(yè)務(wù)場景選擇。
7 命中率問題
緩存的命中率問題,是一個讓人非常頭疼的問題。
前面的章節(jié)已經(jīng)介紹過。
一般情況下,如果有用戶請求過來,先查緩存,如果緩存中存在數(shù)據(jù),則直接返回。
如果緩存中不存在,則再查數(shù)據(jù)庫,如果數(shù)據(jù)庫中存在,則將數(shù)據(jù)放入緩存,然后返回。如果數(shù)據(jù)庫中也不存在,則直接返回失敗。
流程圖如下:
圖片
緩存命中:直接從緩存中獲取數(shù)據(jù)。
緩存不命中:無法從緩存中獲取數(shù)據(jù),而要從數(shù)據(jù)庫獲取其他途徑獲取數(shù)據(jù)。
我們肯定是希望緩存命中率越高越好,這樣接口的性能越好,但實際工作中卻經(jīng)常啪啪打臉。
因為可能會出現(xiàn)緩存不存在,或者緩存過期等問題,導(dǎo)致緩存不能命中。
那么,如何提升緩存的命中率呢?
7.1 緩存預(yù)熱
我們在API服務(wù)啟動之前,可以先用job,將相關(guān)數(shù)據(jù)先保存到緩存中,做預(yù)熱。
這樣后面的用戶請求,就能直接從緩存中獲取數(shù)據(jù),而無需訪問數(shù)據(jù)庫了。
7.2 合理調(diào)整過期時間
有時候,我們給緩存設(shè)置的過期時間太短,導(dǎo)致后面會產(chǎn)生大量的過期緩存。
會導(dǎo)致緩存命中率非常低。
這時需要合理調(diào)整過期時間,比如:之前設(shè)置1秒的,現(xiàn)在改成5秒,10秒,30秒或者1分鐘等等。
7.3 增加緩存內(nèi)存
如果我們部署的Redis服務(wù)器的內(nèi)存太小,很容易出現(xiàn)內(nèi)存不足的情況,從而會頻繁觸發(fā)內(nèi)存淘汰機制。
也會影響緩存的命中率。
這種情況下,我們需要增加緩存內(nèi)存。
緩存的內(nèi)存過小問題,也經(jīng)常會出現(xiàn)。
今天的內(nèi)容先分享到這里,感謝你的閱讀,希望對你會有所幫助。