【大廠面試02期】Redis過期key是怎麼樣清理的?

NotFound9發表於2020-06-03

【大廠面試02期】Redis過期key是怎麼樣清理的?

在Redis中,對於過期key的清理主要有惰性清除,定時清理,記憶體不夠時清理三種方法,下面我們就來具體看看這三種清理方法。

(1)惰性清除

在訪問key時,如果發現key已經過期,那麼會將key刪除。

(2)定時清理

Redis配置項hz定義了serverCron任務的執行週期,預設每次清理時間為25ms,每次清理會依次遍歷所有DB,從db隨機取出20個key,如果過期就刪除,如果其中有5個key過期,那麼就繼續對這個db進行清理,否則開始清理下一個db。

(3)記憶體不夠時清理

當執行寫入命令時,如果發現記憶體不夠,那麼就會按照配置的淘汰策略清理記憶體,淘汰策略一般有6種,Redis4.0版本後又增加了2種,主要由分為三類

  • 第一類 不處理,等報錯(預設的配置)

    • noeviction,發現記憶體不夠時,不刪除key,執行寫入命令時直接返回錯誤資訊。(Redis預設的配置就是noeviction)
  • 第二類 從所有結果集中的key中挑選,進行淘汰

    • allkeys-random 就是從所有的key中隨機挑選key,進行淘汰
    • allkeys-lru 就是從所有的key中挑選最近使用時間距離現在最遠的key,進行淘汰
    • allkeys-lfu 就是從所有的key中挑選使用頻率最低的key,進行淘汰。(這是Redis 4.0版本後新增的策略)
  • 第三類 從設定了過期時間的key中挑選,進行淘汰

    這種就是從設定了expires過期時間的結果集中選出一部分key淘汰,挑選的演算法有:

    • volatile-random 從設定了過期時間的結果集中隨機挑選key刪除。

    • volatile-lru 從設定了過期時間的結果集中挑選上次使用時間距離現在最久的key開始刪除

    • volatile-ttl 從設定了過期時間的結果集中挑選可存活時間最短的key開始刪除(也就是從哪些快要過期的key中先刪除)

    • volatile-lfu 從過期時間的結果集中選擇使用頻率最低的key開始刪除(這是Redis 4.0版本後新增的策略)

LRU演算法

LRU演算法的設計原則是如果一個資料近期沒有被訪問到,那麼之後一段時間都不會被訪問到。所以當元素個數達到限制的值時,優先移除距離上次使用時間最久的元素。

可以使用雙向連結串列Node+HashMap<String, Node>來實現,每次訪問元素後,將元素移動到連結串列頭部,當元素滿了時,將連結串列尾部的元素移除,HashMap主要用於根據key獲得Node以及新增時判斷節點是否已存在和刪除時快速找到節點。

PS:使用單向連結串列能不能實現呢,也可以,單向連結串列的節點雖然獲取不到pre節點的資訊,但是可以將下一個節點的key和value設定在當前節點上,然後把當前節點的next指標指向下下個節點,這樣相當於把下一個節點刪除了

//雙向連結串列
    public static class ListNode {
        String key;//這裡儲存key便於元素滿時,刪除尾節點時可以快速從HashMap刪除鍵值對
        Integer value;
        ListNode pre = null;
        ListNode next = null;
        ListNode(String key, Integer value) {
            this.key = key;
            this.value = value;
        }
    }

    ListNode head;
    ListNode last;
    int limit=4;
    
    HashMap<String, ListNode> hashMap = new HashMap<String, ListNode>();

    public void add(String key, Integer val) {
        ListNode existNode = hashMap.get(key);
        if (existNode!=null) {
            //從連結串列中刪除這個元素
            ListNode pre = existNode.pre;
            ListNode next = existNode.next;
            if (pre!=null) {
               pre.next = next;
            }
            if (next!=null) {
               next.pre = pre;
            }
            //更新尾節點
            if (last==existNode) {
                last = existNode.pre;
            }
            //移動到最前面
            head.pre = existNode;
            existNode.next = head;
            head = existNode;
            //更新值
            existNode.value = val;
        } else {
            //達到限制,先刪除尾節點
            if (hashMap.size() == limit) {
                ListNode deleteNode = last;
                hashMap.remove(deleteNode.key);
              //正是因為需要刪除,所以才需要每個ListNode儲存key
                last = deleteNode.pre;
                deleteNode.pre = null;
                last.next = null;
            }
            ListNode node = new ListNode(key,val);
            hashMap.put(key,node);
            if (head==null) {
                head = node;
                last = node;
            } else {
                //插入頭結點
                node.next = head;
                head.pre = node;
                head = node;
            }
        }

    }

    public ListNode get(String key) {
        return hashMap.get(key);
    }

    public void remove(String key) {
        ListNode deleteNode = hashMap.get(key);
        ListNode preNode = deleteNode.pre;
        ListNode nextNode = deleteNode.next;
        if (preNode!=null) {
            preNode.next = nextNode;
        }
        if (nextNode!=null) {
            nextNode.pre = preNode;
        }
        if (head==deleteNode) {
            head = nextNode;
        }
        if (last == deleteNode) {
            last = preNode;
        }
        hashMap.remove(key);
    }

LFU演算法

LFU演算法的設計原則時,如果一個資料在最近一段時間被訪問的時次數越多,那麼之後被訪問的概率會越大,基本實現是每個資料都有一個引用計數,每次資料被訪問後,引用計數加1,需要淘汰資料時,淘汰引用計數最小的資料。在Redis的實現中,每次key被訪問後,引用計數是加一個介於0到1之間的數p,並且訪問越頻繁p值越大,而且在一定的時間間隔內,
如果key沒有被訪問,引用計數會減少。

最後

大家有什麼想法,歡迎進群一起討論(因為大群已經滿200人了,大家可以掃碼進這個小群,我拉大家進大群)!本文已收錄到1.1K Star數開源學習指南——《大廠面試指北》,如果想要了解更多大廠面試相關的內容,瞭解更多可以看
http://notfound9.github.io/interviewGuide/#/docs/BATInterview

相關文章