1. 程式人生 > >搞定redis面試--Redis的過期策略?手寫一個LRU?

搞定redis面試--Redis的過期策略?手寫一個LRU?

過期策略 問題 protect 惰性 存儲 什麽 不出 結果 true

1 面試題

Redis的過期策略都有哪些?內存淘汰機制都有哪些?手寫一下LRU代碼實現?

2 考點分析

1)我往redis裏寫的數據怎麽沒了?

我們生產環境的redis怎麽經常會丟掉一些數據?寫進去了,過一會兒可能就沒了。
我的天,同學,你問這個問題就說明redis你就沒用對啊。redis是緩存,你給當存儲了是吧?

啥叫緩存?用內存當緩存。內存是無限的嗎,內存是很寶貴而且是有限的,磁盤是廉價而且是大量的。可能一臺機器就幾十個G的內存,但是可以有幾個T的硬盤空間。redis主要是基於內存來進行高性能、高並發的讀寫操作的。

那既然內存是有限的,比如redis就只能用10個G,你要是往裏面寫了20個G的數據,會咋辦?當然會幹掉10個G的數據,然後就保留10個G的數據了。那幹掉哪些數據?保留哪些數據?當然是幹掉不常用的數據,保留常用的數據了。

所以說,這是緩存的一個最基本的概念,數據是會過期的,要麽是你自己設置個過期時間,要麽是redis自己給幹掉。

set key value 過期時間(1小時)

set進去的key,1小時之後就沒了,就失效了

2)我的數據明明都過期了,怎麽還占用著內存啊?

還有一種就是如果你設置好了一個過期時間,你知道redis是怎麽給你弄成過期的嗎?什麽時候刪除掉?如果你不知道,之前有個人就問了,為啥好多數據明明應該過期了,結果發現redis內存占用還是很高?那是因為你不知道redis是怎麽刪除那些過期key的。

redis 內存一共是10g,你現在往裏面寫了5g的數據,結果這些數據明明你都設置了過期時間,要求這些數據1小時之後都會過期,結果1小時之後,你回來一看,redis機器,怎麽內存占用還是50%呢?5g數據過期了,我從redis裏查,是查不到了,結果過期的數據還占用著redis的內存。

如果你連這個問題都不知道,上來就懵了,回答不出來,那線上你寫代碼的時候,想當然的認為寫進redis的數據就一定會存在,後面導致系統各種漏洞和bug,誰來負責?

3 詳解

3.1 設置過期時間

我們set key的時候,都可以給一個expire time,就是過期時間,指定這個key比如說只能存活1個小時?10分鐘?這個很有用,我們自己可以指定緩存到期就失效。

如果假設你設置一個一批key只能存活1個小時,那麽接下來1小時後,redis是怎麽對這批key進行刪除的?

答案是:定期刪除+惰性刪除

所謂定期刪除,指的是redis默認是每隔100ms就隨機抽取一些設置了過期時間的key,檢查其是否過期,如果過期就刪除。假設redis裏放了10萬個key,都設置了過期時間,你每隔幾百毫秒,就檢查10萬個key,那redis基本上就死了,cpu負載會很高的,消耗在你的檢查過期key上了。註意,這裏可不是每隔100ms就遍歷所有的設置過期時間的key,那樣就是一場性能上的災難。實際上redis是每隔100ms隨機抽取一些key來檢查和刪除的。

但是問題是,定期刪除可能會導致很多過期key到了時間並沒有被刪除掉,那咋整呢?所以就是惰性刪除了。這就是說,在你獲取某個key的時候,redis會檢查一下 ,這個key如果設置了過期時間那麽是否過期了?如果過期了此時就會刪除,不會給你返回任何東西。

並不是key到時間就被刪除掉,而是你查詢這個key的時候,redis再懶惰的檢查一下

通過上述兩種手段結合起來,保證過期的key一定會被幹掉。

很簡單,就是說,你的過期key,靠定期刪除沒有被刪除掉,還停留在內存裏,占用著你的內存呢,除非你的系統去查一下那個key,才會被redis給刪除掉。

但是實際上這還是有問題的,如果定期刪除漏掉了很多過期key,然後你也沒及時去查,也就沒走惰性刪除,此時會怎麽樣?如果大量過期key堆積在內存裏,導致redis內存塊耗盡了,咋整?

答案是:走內存淘汰機制。

3.2 內存淘汰

如果redis的內存占用過多的時候,此時會進行內存淘汰,有如下一些策略:

redis 10個key,現在已經滿了,redis需要刪除掉5個key

1個key,最近1分鐘被查詢了100次
1個key,最近10分鐘被查詢了50次
1個key,最近1個小時倍查詢了1次

1)noeviction:當內存不足以容納新寫入數據時,新寫入操作會報錯,這個一般沒人用吧,實在是太惡心了
2)allkeys-lru:當內存不足以容納新寫入數據時,在鍵空間中,移除最近最少使用的key(這個是最常用的)
3)allkeys-random:當內存不足以容納新寫入數據時,在鍵空間中,隨機移除某個key,這個一般沒人用吧,為啥要隨機,肯定是把最近最少使用的key給幹掉啊
4)volatile-lru:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,移除最近最少使用的key(這個一般不太合適)
5)volatile-random:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,隨機移除某個key
6)volatile-ttl:當內存不足以容納新寫入數據時,在設置了過期時間的鍵空間中,有更早過期時間的key優先移除

百度,問題啊,網上魚龍混雜

如果百度一些api操作,入門的知識,ok的,隨便找一個博客都可以

一些高級別的,redis單線程模型

很簡單,你寫的數據太多,內存滿了,或者觸發了什麽條件,redis lru,自動給你清理掉了一些最近很少使用的數據

3.3 要不你手寫一個LRU算法?

確實有時會問這個,因為有些候選人如果確實過五關斬六將,前面的問題都答的很好,那麽其實讓他寫一下LRU算法,可以考察一下編碼功底

你可以現場手寫最原始的LRU算法,那個代碼量太大了,不太現實

public class LRUCache<K, V> extends LinkedHashMap<K, V> {
    
private final int CACHE_SIZE;

    // 這裏就是傳遞進來最多能緩存多少數據
    public LRUCache(int cacheSize) {
        super((int) Math.ceil(cacheSize / 0.75) + 1, 0.75f, true); // 這塊就是設置一個hashmap的初始大小,同時最後一個true指的是讓linkedhashmap按照訪問順序來進行排序,最近訪問的放在頭,最老訪問的就在尾
        CACHE_SIZE = cacheSize;
    }

    @Override
    protected boolean removeEldestEntry(Map.Entry eldest) {
        return size() > CACHE_SIZE; // 這個意思就是說當map中的數據量大於指定的緩存個數的時候,就自動刪除最老的數據
    }

}
我給你看上面的代碼,是告訴你最起碼你也得寫出來上面那種代碼,不求自己純手工從底層開始打造出自己的LRU,但是起碼知道如何利用已有的jdk數據結構實現一個java版的LRU。

搞定redis面試--Redis的過期策略?手寫一個LRU?