Redis面試必問的過期策略有哪些?Expire&LRU

一、帶着疑問看文章

不知道你有沒有遇到以下情形?

  • 我往Redis裏寫的數據怎麼沒了?偶爾會丟失部分數據
  • 我的數據明明都過期了,怎麼還佔用着內存?

二、過期策略&LRU

可以回答:我往Redis裏寫的數據怎麼沒了?偶爾會丟失部分數據這個問題

如果redis的內存佔用過多的時候,此時會進行內存淘汰策略:
假設如下場景:

redis裏有10000個key,現在已經滿了,redis需要觸發內存淘汰策略刪除key
1個key,最近1min被查了2w次
1個key,最近10min被查了100次
1個key,最近1小時被查了10次
  • noeviction:當內存不足以寫新數據的時候,那麼寫操作會報錯。(一般沒人用,畢竟只是緩存,又不是mysql這種ACID的關係型數據庫)
  • allkeys-lru:當內存不足以寫新數據的時候,在key空間中,移除最近最少使用的key(這個是最常用的),如果只移除一個的話,那麼最後一個key肯定被移除
  • allkeys-random:當內存不足以寫新數據的時候,在key空間中,隨機移除某個key(一般沒人用,萬一把我活躍的key移除了咋辦)
  • volatile-lru:當內存不足以寫新數據的時候,在設置了過期時間的key中,移除最近最少使用的key(這個也還湊合,但是也不咋地,都設置過期時間了,你還給我移除幹嘛)
  • volatile-random:當內存不足以寫新數據的時候,在設置了過期時間的鍵空間中,隨機移除某個key(極其不合適,還不如allkeys-random)
  • volatile-ttl:當內存不足以寫新數據的時候,在設置了過期時間的鍵空間中,有更早過期時間的key優先移除(也不咋合適,不一定更早過期的就是不活躍key,而且都快過期了你還給我移除幹嘛,自生自滅)

三、expire原理

可以回答:我的數據明明都過期了,怎麼還佔用着內存?
官方:http://redis.cn/commands/expire.html

如果假設你設置一個一批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內存塊耗盡了,咋整?

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

四、手寫簡易版LRU

不求自己純手工從底層開始打造出自己的LRU,但是起碼知道如何利用已有的jdk數據結構實現一個java版的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中的數據量大於指定的緩存個數的時候,就自動刪除最老的數據
    }
}

五、總結

  • 我往Redis裏寫的數據怎麼沒了?偶爾會丟失部分數據
  • 我的數據明明都過期了,怎麼還佔用着內存?
  • 手寫簡易版LRU

六、個人公衆號

微信公衆號【Java碼農社區】
在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章