Введение
В случае большого количества данных есть два способа найти информацию Ключа, соответствующую определенному правилу:
- команда keys: простая и грубая, но поскольку Redis однопоточный, то команда keys выполняется блокирующим образом, а сложность ключей реализована обходным способом, сложность O(n), чем больше ключей в Библиотека Redis, стоимость поиска реализации Чем больше значение, тем больше время блокировки.
- Команда can: реализует поиск значения ключа неблокирующим способом, в большинстве случаев может заменить команду keys, а опциональность сильнее.
1. Сканировать связанные команды
Оба используются для постепенного перебора элементов коллекции.
- Команда SCAN используется для перебора ключей базы данных в текущей базе данных.
- Команда SSCAN используется для перебора элементов в ключе коллекции.
- Команда HSCAN используется для перебора пар ключ-значение в хеш-ключе.
- Команда 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: Предоставляя аргумент шаблона в стиле шара, команда возвращает только те элементы, которые соответствуют заданному шаблону.
Предупреждение о высокой энергии вперед: Базовая операция сопоставления выполняется в течение периода времени перед возвратом элемента клиенту после того, как элемент удален из набора данных. Если в возвращаемом наборе результатов нет совпадения, он может не возвращать какой-либо элемент при многократном выполнении. .
Таким образом, результат одиночного возврата пуст означает не конец обхода, а возможность увидеть, равно ли возвращенное значение курсора нулю;
2.3 Количество интерпретаций
count: вы можете контролировать максимальное количество результатов, возвращаемых каждый раз, count — это просто подсказка, а возвращаемых результатов может быть больше или меньше.
Уведомление
- возвращениеРезультаты могут дублироваться, который необходимо повторить клиенту, что очень важно;
- Если в процессе обхода происходит модификация данных, неясно, можно ли пройти по измененным данным;
3. Практический
В сети иногда нужно удалить большое количество ключей, есть несколько моментов риска:
- Большое количество ключей, указанных в одноразовом запросе, может привести к зависанию службы Redis. Redis — это однопоточная программа, которая выполняет все инструкции последовательно. Другие инструкции должны дождаться выполнения инструкции с текущими ключами, прежде чем продолжить.
- Как из большого количества ключей найти ключ, удовлетворяющий определенному префиксу?
Но с помощью сканирования мы можем указать общие ключи и указать условия одноразового запроса.
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);
}
}