1.數(shù)據(jù)為什么會過期火诸?
首先躲查,要明白redis是用來做數(shù)據(jù)緩存的它浅,不是用來做數(shù)據(jù)存儲的(當(dāng)然也可以當(dāng)數(shù)據(jù)庫用),所以數(shù)據(jù)時候過期的镣煮,過期的數(shù)據(jù)就不見了姐霍,過期主要有兩種情況,
①在設(shè)置緩存數(shù)據(jù)時制定了過期時間,這樣到了過期時間數(shù)據(jù)就不見了镊折。
②redis的數(shù)據(jù)是存放在內(nèi)存中的胯府,而內(nèi)存是有限的,是不可能放過多數(shù)據(jù)的恨胚,比如只有10G的內(nèi)存骂因,想要向里面放入20G的數(shù)據(jù),那么就注定會有10G的數(shù)據(jù)會丟失赃泡。
2.redis的過期策略是什么樣的寒波?
redis采用了 “定期刪除+惰性刪除” 的過期策略。
①定期刪除
原理:定期刪除指的是redis默認(rèn)每隔100ms就隨機抽取一些設(shè)置了過期時間的key升熊,檢測這些key是否過期俄烁,如果過期了就將其刪掉。
為什么會選擇一部分僚碎,而不是全部:因為如果這是redis里面有大量的key都設(shè)置了過期時間猴娩,那么如果全部去檢測一遍,CPU負(fù)載就會很高勺阐,會浪費大量的時間在檢測上面卷中,甚至直接導(dǎo)致redis掛掉。所有只會抽取一部分而不會全部檢查渊抽。
出現(xiàn)問題:這樣的話就會出現(xiàn)大量的已經(jīng)過期的key并沒有被刪除蟆豫,這就是 為什么有時候大量的key明明已經(jīng)過了失效時間,但是redis的內(nèi)存還是被大量占用的原因 懒闷,為了解決這個問題十减,就需要 惰性刪除 這個策略了。
②惰性刪除
原理:惰性刪除不在是redis去主動刪除愤估,而是在你要獲取某個key 的時候帮辟,redis會先去檢測一下這個key是否已經(jīng)過期,如果沒有過期則返回給你玩焰,如果已經(jīng)過期了由驹,那么redis會刪除這個key,不會返回給你昔园。
這樣兩種策略就保證了 過期的key最終一定會被刪除掉 蔓榄,但是這只是保證了最終一定會被刪除,要是定時刪除漏掉了大量過期的key默刚,而且我們也沒有及時的去訪問這些key甥郑,那么這些key不就不會被刪除了嗎?不就會一直占著我們的內(nèi)存嗎?這樣不還是會導(dǎo)致redis內(nèi)存耗盡嗎荤西?
由于存在這樣的問題澜搅,所以redis引入了 內(nèi)存淘汰機制 來解決伍俘。
3.內(nèi)存淘汰機制
內(nèi)存淘汰機制就保證了在redis的內(nèi)存占用過多的時候,去進行內(nèi)存淘汰店展,也就是刪除一部分key养篓,保證redis的內(nèi)存占用率不會過高,那么它會刪除那些key呢赂蕴?
redis提供了6中內(nèi)存淘汰策略柳弄,我們可以去進行選擇,六中策略如下:
①noeviction:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時概说,新寫入操作會報錯碧注,無法寫入新數(shù)據(jù),一般不采用糖赔。
②allkeys-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時萍丐,在鍵空間中,移除最近最少使用的key放典,這個是最常用的逝变。
③allkeys-random:當(dāng)內(nèi)存不足以容納新寫入的數(shù)據(jù)時,在鍵空間中奋构,隨機移除key壳影,一般也不使用。
④volatile-lru:volatile-lru:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時弥臼,在設(shè)置了過期時間的鍵空間中宴咧,移除最近最少使用的key(這個一般不太合適) 。
⑤volatile-random:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時径缅,在設(shè)置了過期時間的鍵空間中掺栅,隨機移除某個key 。
⑥volatile-ttl:當(dāng)內(nèi)存不足以容納新寫入數(shù)據(jù)時纳猪,在設(shè)置了過期時間的鍵空間中氧卧,有更早過期時間的key優(yōu)先移除。
4.手寫一個LRU算法
//基于JavaLinkedHashMap實現(xiàn)
public class LRUCache<K,V> extends LinkedHashMap<K,V>{
private final int CACHE_SIZE;
//保存?zhèn)鬟f進來的最大數(shù)據(jù)量
public LRUCache(int cacheSize){
//設(shè)置hashmap的初始大小氏堤,同時最后一個true指的是讓linkedhashmap按照訪問順序來進行排序沙绝,
//最近訪問的放在頭,最老訪問的放在尾
super((int)Math.ceil(cacheSize/0.75)+1,0.75f,true);
CACHE_SIZE = CacheSize丽猬;
}
@Override
protected boolean removeEldestEntry(Map.Entry eldest){
//當(dāng)map中的數(shù)據(jù)量大于指定的緩存?zhèn)€數(shù)的時候宿饱,就自動刪除最老的數(shù)據(jù)熏瞄。
return size() > CACHE_SIZE;
}
}