Как атомно удалять ключи, соответствующие шаблону, используя Redis

В моей Redis DB у меня есть ряд хешей prefix:<numeric_id>.

Иногда я хочу их продуть атомарно. Как это сделать без использования какого-либо распределенного механизма блокировки?

Ответ 1

Начиная с redis 2.6.0, вы можете запускать сценарии lua, которые выполняются атомарно. Я никогда не писал, но я думаю, что это будет выглядеть примерно так

EVAL "return redis.call('del', unpack(redis.call('keys', ARGV[1])))" 0 prefix:[YOUR_PREFIX e.g delete_me_*]

Смотрите документацию EVAL.

Ответ 2

Выполнить в bash:

redis-cli KEYS "prefix:*" | xargs redis-cli DEL

UPDATE

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

У вас есть такие значения:

prefix_prefix_actuall = 2
prefix:2:1 = 4
prefix:2:2 = 10

Когда вам нужно очистить данные, вы сначала меняете prefix_actuall (например, set prefix_prefix_actuall = 3), поэтому ваше приложение будет записывать новые данные в префикс ключей: 3: 1 и префикс: 3: 2. Затем вы можете безопасно взять старые значения из префикса: 2: 1 и префикс: 2: 2 и очистить старые ключи.

Ответ 3

Здесь полностью работающая и атомарная версия удаленной замены, реализованная в Lua. Он будет работать намного быстрее, чем версия xargs из-за гораздо меньшей загрузки сети и полностью атомарной, блокируя любые другие запросы против redis до тех пор, пока он не завершится. Если вы хотите атомически удалить ключи на Redis 2.6.0 или выше, это определенно путь:

redis-cli -n [some_db] -h [some_host_name] EVAL "return redis.call('DEL', unpack(redis.call('KEYS', ARGV[1] .. '*')))" 0 prefix:

Это рабочая версия идеи @mcdizzle в его ответе на этот вопрос. Кредит за идею 100% идет к нему.

РЕДАКТИРОВАТЬ: В комментарии на Kikito ниже, если у вас есть больше ключей для удаления, чем свободная память на сервере Redis, вы столкнетесь с "слишком много элементов для распаковки" . В этом случае выполните:

for _,k in ipairs(redis.call('keys', ARGV[1])) do 
    redis.call('del', k) 
end

Как предложил Кикито.

Ответ 4

Отказ от ответственности: следующее решение не предоставляет атомарность.

Начиная с v2.8 вы действительно хотите использовать команду SCAN вместо KEYS [1]. Следующий Bash script демонстрирует удаление ключей по шаблону:

#!/bin/bash

if [ $# -ne 3 ] 
then
  echo "Delete keys from Redis matching a pattern using SCAN & DEL"
  echo "Usage: $0 <host> <port> <pattern>"
  exit 1
fi

cursor=-1
keys=""

while [ $cursor -ne 0 ]; do
  if [ $cursor -eq -1 ]
  then
    cursor=0
  fi

  reply=`redis-cli -h $1 -p $2 SCAN $cursor MATCH $3`
  cursor=`expr "$reply" : '\([0-9]*[0-9 ]\)'`
  keys=${reply##[0-9]*[0-9 ]}
  redis-cli -h $1 -p $2 DEL $keys
done

[1] KEYS - опасная команда, которая может привести к DoS. Ниже приведена цитата со страницы документации:

Предупреждение: рассмотрите KEYS как команду, которая должна использоваться исключительно в производственных средах с особой осторожностью. Он может испортить производительность, когда он выполняется против больших баз данных. Эта команда предназначена для отладки и специальных операций, таких как изменение макета вашего ключа. Не используйте KEYS в вашем обычном коде приложения. Если вы ищете способ поиска ключей в подмножестве своего пространства ключей, подумайте об использовании наборов.

UPDATE: один лайнер для одного и того же базового эффекта -

$ redis-cli --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli DEL

Ответ 5

Для тех, у кого были проблемы с разбором других ответов:

eval "for _,k in ipairs(redis.call('keys','key:*:pattern')) do redis.call('del',k) end" 0

Замените key:*:pattern вашим собственным шаблоном и введите его в redis-cli и все redis-cli.

Кредит Лиско от: http://redis.io/commands/del

Ответ 6

Я использую команду ниже в redis 3.2.8

redis-cli KEYS *YOUR_KEY_PREFIX* | xargs redis-cli DEL

Вы можете получить дополнительную помощь, связанную с поиском шаблонов клавиш отсюда: - https://redis.io/commands/keys. Используйте свой удобный шаблон в стиле глобуса в соответствии с вашим требованием, например *YOUR_KEY_PREFIX* или YOUR_KEY_PREFIX?? или любым другим.

И если у вас есть интегрированная библиотека PHP Redis, то ниже вам поможет функция.

flushRedisMultipleHashKeyUsingPattern("*YOUR_KEY_PATTERN*"); //function call

function flushRedisMultipleHashKeyUsingPattern($pattern='')
        {
            if($pattern==''){
                return true;
            }

            $redisObj = $this->redis;
            $getHashes = $redisObj->keys($pattern);
            if(!empty($getHashes)){
                $response = call_user_func_array(array(&$redisObj, 'del'), $getHashes); //setting all keys as parameter of "del" function. Using this we can achieve $redisObj->del("key1","key2);
            }
        }

Спасибо:)

Ответ 7

Решение

@mcdizle не работает, оно работает только для одной записи.

Этот файл работает для всех ключей с одним и тем же префиксом

EVAL "for i, name in ipairs(redis.call('KEYS', ARGV[1])) do redis.call('DEL', name); end" 0 prefix*

Примечание. Вы должны заменить префикс на свой префикс ключа...

Ответ 8

Вы также можете использовать эту команду для удаления ключей: -

Предположим, что в вашем Redis есть много типов ключей like-

  1. 'Xyz_category_fpc_12'
  2. 'Xyz_category_fpc_245'
  3. 'Xyz_category_fpc_321'
  4. 'Xyz_product_fpc_876'
  5. 'Xyz_product_fpc_302'
  6. 'Xyz_product_fpc_01232'

Ex- " xyz_category_fpc " здесь xyz - это имя сайта, и эти ключи относятся к продуктам и категориям сайта электронной коммерции и генерируются FPC.

Если вы используете эту команду как below-

redis-cli --scan --pattern 'key*' | xargs redis-cli del

ИЛИ ЖЕ

redis-cli --scan --pattern 'xyz_category_fpc*' | xargs redis-cli del

Он удаляет все ключи, такие как " xyz_category_fpc " (удаляет ключи 1, 2 и 3). Для удаления других 4, 5 и 6 цифровых клавиш используйте команду " xyz_product_fpc " в приведенной выше команде.

Если вы хотите удалить все в Redis, то следуйте этим Commands-

С редис-кли:

  1. FLUSHDB - удаляет данные из вашей базы данных CURRENT подключения.
  2. FLUSHALL - Удаляет данные из ВСЕХ баз данных.

Например: - в вашей оболочке:

redis-cli flushall
redis-cli flushdb

Ответ 9

Если у вас есть пробел в имени клавиш, вы можете использовать его в bash:

redis-cli keys "pattern: *" | xargs -L1 -I '$' echo '"$"' | xargs redis-cli del

Ответ 10

Ответ на @itamar велик, но разбор ответа не работал у меня, особенно. в случае отсутствия ключей, обнаруженных в данном сканировании. Возможно, более простое решение, прямо с консоли:

redis-cli -h HOST -p PORT  --scan --pattern "prefix:*" | xargs -n 100 redis-cli DEL

Это также использует SCAN, который предпочтительнее KEYS в производстве, но не является атомарным.

Ответ 11

У меня была такая же проблема. Я сохранил данные сеанса для пользователя в формате:

session:sessionid:key-x - value of x
session:sessionid:key-y - value of y
session:sessionid:key-z - value of z

Таким образом, каждая запись представляла собой отдельную пару "ключ-значение". Когда сеанс уничтожен, я хотел удалить все данные сеанса, удалив ключи с помощью шаблона session:sessionid:*, но redis не имеет такой функции.

Что я сделал: сохраните данные сеанса в hash. Я просто создаю хэш с хеш-идентификатором session:sessionid, а затем нажимаю key-x, key-y, key-z в этом хэше (для меня это не имеет значения), и если мне не нужен этот хеш, я просто делаю a DEL session:sessionid, и все данные, связанные с этим идентификатором хэша, исчезли. DEL является атомарным и доступ к данным/записи данных в хеш-это O (1).

Ответ 13

FYI.

  • только с помощью bash и redis-cli
  • не используется keys (используется scan)
  • хорошо работает в режиме кластера
  • не атомный

Возможно, вам нужно только изменить символы капитала.

scan-match.sh

#!/bin/bash
rcli="/YOUR_PATH/redis-cli" 
default_server="YOUR_SERVER"
default_port="YOUR_PORT"
servers=`$rcli -h $default_server -p $default_port cluster nodes | grep master | awk '{print $2}' | sed 's/:.*//'`
if [ x"$1" == "x" ]; then 
    startswith="DEFAULT_PATTERN"
else
    startswith="$1"
fi
MAX_BUFFER_SIZE=1000
for server in $servers; do 
    cursor=0
    while 
        r=`$rcli -h $server -p $default_port scan $cursor match "$startswith*" count $MAX_BUFFER_SIZE `
        cursor=`echo $r | cut -f 1 -d' '`
        nf=`echo $r | awk '{print NF}'`
        if [ $nf -gt 1 ]; then
            for x in `echo $r | cut -f 1 -d' ' --complement`; do 
                echo $x
            done
        fi
        (( cursor != 0 ))
    do
        :
    done
done

clear-redis-key.sh

#!/bin/bash
STARTSWITH="$1"

RCLI=YOUR_PATH/redis-cli
HOST=YOUR_HOST
PORT=6379
RCMD="$RCLI -h $HOST -p $PORT -c "

./scan-match.sh $STARTSWITH | while read -r KEY ; do
    $RCMD del $KEY 
done

Запуск в bash prompt

$ ./clear-redis-key.sh key_head_pattern

Ответ 14

Это просто реализуется через функцию "Удалить ветку" в FastoRedis, просто выберите ветку, которую вы хотите удалить.

enter image description here

Ответ 15

Пожалуйста, используйте эту команду и попробуйте:

redis-cli --raw keys "$PATTERN" | xargs redis-cli del

Ответ 16

Это не прямой ответ на вопрос, но поскольку я пришел сюда, когда искал свои собственные ответы, я поделюсь этим здесь.

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

Следующий подход, несомненно, некрасиво, но я не нашел лучшего. Атомность здесь не может быть и речи, в этом случае главная цель - сохранить Redis до 100% времени. Он будет работать отлично, если у вас есть все ваши ключи в одной из баз данных, и вам не нужно сопоставлять какой-либо шаблон, но не можете использовать http://redis.io/commands/FLUSHDB из-за того, что он блокирует природу.

Идея проста: напишите script, который выполняется в цикле и использует операцию O (1), например http://redis.io/commands/SCAN или http://redis.io/commands/RANDOMKEY, чтобы получить ключи, проверяет, соответствуют ли они шаблону (если вам это нужно) и http://redis.io/commands/DEL их по одному.

Если есть лучший способ сделать это, пожалуйста, дайте мне знать, я обновлю ответ.

Пример реализации со случайным ключом в Ruby, как задача rake, неблокирующая замена чего-то вроде redis-cli -n 3 flushdb:

desc 'Cleanup redis'
task cleanup_redis: :environment do
  redis = Redis.new(...) # connection to target database number which needs to be wiped out
  counter = 0
  while key = redis.randomkey               
    puts "Deleting #{counter}: #{key}"
    redis.del(key)
    counter += 1
  end
end

Ответ 17

Версия, использующая SCAN, а не KEYS (как рекомендовано для производственных серверов) и --pipe, а не xargs.

Я предпочитаю использовать трубку поверх xargs, потому что она более эффективна и работает, когда ваши ключи содержат кавычки или другие специальные символы, которые ваша оболочка пытается и интерпретирует. Подстановка регулярного выражения в этом примере обертывает ключ в двойные кавычки и исключает любые двойные кавычки внутри.

export REDIS_HOST=your.hostname.com
redis-cli -h "$REDIS_HOST" --scan --pattern "YourPattern*" > /tmp/keys
time cat /tmp/keys | perl -pe 's/"/\\"/g;s/^/DEL "/;s/$/"/;'  | redis-cli -h "$REDIS_HOST" --pipe

Ответ 18

Я поддерживаю все ответы, связанные с наличием какого-либо инструмента или выполнением выражения Lua.

Еще один вариант с моей стороны:

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

С этой целью я пишу java-клиентский инструмент, который делает все это. В случае удаления ключей утилита может быть очень простой, только один класс:

public class DataCleaner {

    public static void main(String args[]) {
        String keyPattern = args[0];
        String host = args[1];
        int port = Integer.valueOf(args[2]);
        int dbIndex = Integer.valueOf(args[3]);

        Jedis jedis = new Jedis(host, port);

        int deletedKeysNumber = 0;
        if(dbIndex >= 0){
            deletedKeysNumber += deleteDataFromDB(jedis, keyPattern, dbIndex);
        } else {
            int dbSize = Integer.valueOf(jedis.configGet("databases").get(1));
            for(int i = 0; i < dbSize; i++){
                deletedKeysNumber += deleteDataFromDB(jedis, keyPattern, i);
            }
        }

        if(deletedKeysNumber == 0) {
            System.out.println("There is no keys with key pattern: " + keyPattern + " was found in database with host: " + host);
        }
    }

    private static int deleteDataFromDB(Jedis jedis, String keyPattern, int dbIndex) {
        jedis.select(dbIndex);
        Set<String> keys = jedis.keys(keyPattern);
        for(String key : keys){
            jedis.del(key);
            System.out.println("The key: " + key + " has been deleted from database index: " + dbIndex);
        }

        return keys.size();
    }

}

Ответ 19

Я попробовал большинство методов, упомянутых выше, но они не спомогли мне, после некоторых поисков я нашел следующие пункты:

  • если у вас есть более 1 дБ на Redis, вы должны определить базу данных, используя -n [number]
  • если у вас есть несколько ключей, используйте del но если есть тысячи или миллионы ключей, лучше использовать unlink потому что unlink неблокирует, а del блокирует, для получения дополнительной информации посетите эту страницу unlink vs del
  • также keys как дель и блокирует

поэтому я использовал этот код для удаления ключей по шаблону:

 redis-cli -n 2 --scan --pattern '[your pattern]' | xargs redis-cli -n 2 unlink 

Ответ 20

бедный человек атомная масса-удалить?

Возможно, вы могли бы установить их все в EXPIREAT за ту же секунду - например, через несколько минут в будущем, - а затем подождать до этого времени и одновременно увидеть все "самоуничтожение".

но я не уверен, насколько это будет атом.

Ответ 21

Spring Функция RedisTemplate обеспечивает функциональность. В последней версии RedissonClient устарела функциональность "deleteByPattern".

Set<String> keys = redisTemplate.keys("geotag|*");
redisTemplate.delete(keys);

Ответ 22

redis-cli keys "*prefix*" работа для меня