Расширенная операция Redis: сканирование реализует нечеткий запрос

Redis

Введение

В случае большого количества данных есть два способа найти информацию Ключа, соответствующую определенному правилу:

  1. команда keys: простая и грубая, но поскольку Redis однопоточный, то команда keys выполняется блокирующим образом, а сложность ключей реализована обходным способом, сложность O(n), чем больше ключей в Библиотека Redis, стоимость поиска реализации Чем больше значение, тем больше время блокировки.
  2. Команда can: реализует поиск значения ключа неблокирующим способом, в большинстве случаев может заменить команду keys, а опциональность сильнее.

1. Сканировать связанные команды

Оба используются для постепенного перебора элементов коллекции.

  1. Команда SCAN используется для перебора ключей базы данных в текущей базе данных.
  2. Команда SSCAN используется для перебора элементов в ключе коллекции.
  3. Команда HSCAN используется для перебора пар ключ-значение в хеш-ключе.
  4. Команда ZSCAN используется для перебора элементов (включая членов элементов и оценки элементов) в отсортированном наборе.

В следующих примерах в качестве примера будет использоваться sscan.

2. Параметры команды

redis 127.0.0.1:6379> SSCAN key cursor [MATCH pattern] [COUNT count]

Ключ: соответствующее имя коллекции для запроса.

курсор: значение курсора, первая итерация использует 0 в качестве курсора, что означает начать новую итерацию

[Шаблон MATCH]: нечеткое совпадение

[COUNT count]: количество запросов каждый раз, значение по умолчанию — 10.

2.1 Создайте массив

新建数组

В приведенном выше примере первая итерация использует 0 в качестве курсора для начала новой итерации.

Вторая итерация использует курсор, возвращенный в первой итерации, который является значением первого элемента - 3 , возвращаемого командой.

Начните новую итерацию с 0 в качестве курсора и продолжайте вызывать команду SCAN до тех пор, пока команда не вернет курсор 0. Мы называем этот процесс полной итерацией.

В то же время любое количество клиентов может перебирать один и тот же набор данных.Каждый раз, когда клиент выполняет итерацию, ему нужно передать курсор, и после выполнения итерации получается новый курсор, и этот курсор содержит итерация всех состояний, поэтому серверу не нужно записывать какое-либо состояние для итерации, а также он не блокирует поток

2.2 Интерпретация матча

match: Предоставляя аргумент шаблона в стиле шара, команда возвращает только те элементы, которые соответствуют заданному шаблону.

Предупреждение о высокой энергии вперед: Базовая операция сопоставления выполняется в течение периода времени перед возвратом элемента клиенту после того, как элемент удален из набора данных. Если в возвращаемом наборе результатов нет совпадения, он может не возвращать какой-либо элемент при многократном выполнении. .

Таким образом, результат одиночного возврата пуст означает не конец обхода, а возможность увидеть, равно ли возвращенное значение курсора нулю;

匹配包含o的对象和以o开头的

2.3 Количество интерпретаций

count: вы можете контролировать максимальное количество результатов, возвращаемых каждый раз, count — это просто подсказка, а возвращаемых результатов может быть больше или меньше.

Уведомление

  1. возвращениеРезультаты могут дублироваться, который необходимо повторить клиенту, что очень важно;
  2. Если в процессе обхода происходит модификация данных, неясно, можно ли пройти по измененным данным;

3. Практический

В сети иногда нужно удалить большое количество ключей, есть несколько моментов риска:

  1. Большое количество ключей, указанных в одноразовом запросе, может привести к зависанию службы Redis. Redis — это однопоточная программа, которая выполняет все инструкции последовательно. Другие инструкции должны дождаться выполнения инструкции с текущими ключами, прежде чем продолжить.
  2. Как из большого количества ключей найти ключ, удовлетворяющий определенному префиксу?

Но с помощью сканирования мы можем указать общие ключи и указать условия одноразового запроса.

for (String cacheName : cacheNames) {
        String keyPrefix = new String(cachePrefix.prefix(cacheName)); //拼接我们的
        ScanParams scanParams = new ScanParams().match(keyPrefix.concat("*")).count(200);  //指定规则
        String cur = ScanParams.SCAN_POINTER_START; //游标初始值为0
        boolean hasNext = true;
        int count = 0;
        while (hasNext) {
            count++;
            ScanResult<String> scanResult = jedisCluster.scan(cur, scanParams); //key的正则表达式
            List<String> keys = scanResult.getResult();
            for (String key : keys) {
                jedisCluster.del(key);
            }
            cur = scanResult.getStringCursor();  //返回用于下次遍历的游标
            if (StringUtils.equals("0", cur)) { //说明遍历已结束
                hasNext = false;
            }
        }
        log.info("redis cache evict {} {}", cacheName, count);
    }
}