Как очистить ключи с истекшим сроком действия в Redis?

Java

Как очистить ключи с истекшим сроком действия в Redis?

В Redis в основном существует три метода очистки ключей с истекшим сроком действия: ленивая очистка, обычная очистка и очистка при нехватке памяти.Давайте подробно рассмотрим эти три метода очистки.

(1) Инертная очистка

При доступе к ключу, если будет обнаружено, что срок действия ключа истек, ключ будет удален.

(2) Регулярная очистка

Элемент конфигурации Redis hz определяет период выполнения задачи serverCron. Время по умолчанию для каждой очистки составляет 25 мс. Каждая очистка будет проходить по всем БД по очереди и случайным образом извлекать 20 ключей из БД. Если срок их действия истечет, они будут удалены , Если срок действия 5 ключей истек, то продолжайте очистку этой БД, иначе начните очистку следующей БД.

(3) Очистка, когда памяти недостаточно

При выполнении команды записи, если обнаружится, что памяти недостаточно, память будет очищена в соответствии с настроенной стратегией устранения.Обычно существует 6 стратегий устранения, а после Redis 4.0 добавляются 2, которые в основном делятся на три категории. .

  • Первая категория не обрабатывается и сообщается об ошибке (конфигурация по умолчанию)

    • noeviction, когда обнаруживается, что памяти недостаточно, ключ не удаляется, а сразу возвращается сообщение об ошибке при выполнении команды записи. (Конфигурация Redis по умолчанию — noeviction)
  • Второй тип выбирается из всех ключей результирующего набора и исключается.

    • allkeys-random - это случайный выбор ключей из всех ключей и их удаление.
    • allkeys-lru — выбрать ключ, самое последнее время использования которого наиболее далеко от настоящего из всех ключей, и удалить его.
    • allkeys-lfu заключается в том, чтобы выбрать из всех ключей наименее часто используемый ключ и исключить его. (Это новая стратегия после версии Redis 4.0)
  • Третий тип выбирается из ключей с установленным сроком годности и исключается

    Это необходимо для выбора части ключей, которые необходимо исключить из набора результатов с установленным временем истечения срока действия.Выбранные алгоритмы:

    • volatile-random случайным образом выбирает из результирующего набора ключи со сроком действия и удаляет их.

    • volatile-lru выбирает ключ, время последнего использования которого является самым длинным из настоящего из набора результатов с установленным временем истечения срока действия, и начинает удалять

    • volatile-ttl выбирает ключ с кратчайшим сроком действия из набора результатов с установленным временем истечения и начинает удалять (то есть удалять из ключей, срок действия которых истекает первым)

    • volatile-lfu выбирает наименее часто используемый ключ из результирующего набора времени истечения и начинает удалять (это новая политика после версии Redis 4.0)

Алгоритм LRU

Принцип разработки алгоритма LRU заключается в том, что если к части данных не было доступа в последнее время, доступ к ним не будет осуществляться в течение определенного периода времени. Следовательно, когда количество элементов достигает предельного значения, предпочтительно удаляется элемент с наибольшим временем с момента последнего использования.

Это можно реализовать с помощью двусвязного списка Node+HashMap.После каждого доступа к элементу перемещать элемент в начало связанного списка.Когда элемент заполнен, удалить элемент в конце связанный список.HashMap в основном используется для получения по ключу.узла и быстрого нахождения узла при добавлении и оценке того, существует ли узел уже и удалении.

PS: Можно ли использовать отдельно связанный список? Это также возможно. Хотя узлы по отдельно связанным списком не могут получить информацию о предварительном узле, ключ и значение следующего узла можно установить на текущем узле, А затем можно установить следующий узел текущего узла. Указатель указывает на следующий узел, который эквивалентен удалению следующего узла

//双向链表
    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 после доступа к каждому ключу счетчик ссылок увеличивается на число p от 0 до 1, и чем чаще доступ, тем больше значение p, и в течение определенного интервала времени Если к ключу нет доступа, счетчик ссылок уменьшается.

наконец

Если у вас есть какие-либо идеи, вы можете присоединиться к группе, чтобы обсудить, учиться и развиваться! (Поскольку в большой группе уже 200 человек, вы можете перейти к небольшой группе ниже, я соберу всех в большую группу)

Обзор содержания сухих грузов:

[Интервью с Дачангом, выпуск 01] Как обеспечить согласованность кэша и базы данных в сценариях с высокой степенью параллелизма?

[Интервью с Дачангом, выпуск 02] Как очистить ключи Redis с истекшим сроком действия?

[Интервью с Дачангом 03] Как MySQL решает проблему фантомного чтения?

[Интервью с Дачангом 04] Расскажите о том, как выполняется оператор обновления MySQL?

[Интервью с Дачангом, выпуск 05] Расскажите, как вы понимаете блокировку в MySQL?

[Интервью с Дачангом 06] Расскажите о своем понимании сохраняемости Redis?

[Интервью с Дачангом, выпуск 07] Расскажите, как вы понимаете синхронизированные блокировки?

[Интервью с Дачангом 08] Расскажите о своем понимании HashMap?