Como excluir atomicamente as teclas que combinam com um padrão usando Redis

StackOverflow https://stackoverflow.com/questions/4006324

  •  25-09-2019
  •  | 
  •  

Pergunta

No meu Redis DB, eu tenho vários prefix:<numeric_id> hashes.

Às vezes eu quero expulsá -los todos atomicamente. Como faço isso sem usar algum mecanismo de travamento distribuído?

Foi útil?

Solução

Começando com o Redis 2.6.0, você pode executar scripts Lua, que executam atomicamente. Eu nunca escrevi um, mas acho que seria algo assim

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

Veja o Avaliar documentação.

Outras dicas

Execute em Bash:

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

ATUALIZAR

OK eu entendi. Que tal essa maneira: armazene o prefixo incremental adicional atual e adicione -o a todas as suas teclas. Por exemplo:

Você tem valores como este:

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

Quando você precisa purgar os dados, altera o prefix_actuall primeiro (por exemplo, defina prefix_prefix_actuall = 3), para que seu aplicativo escreva novos dados para as chaves prefixo: 3: 1 e prefixo: 3: 2. Em seguida, você pode pegar valores antigos com segurança do prefixo: 2: 1 e prefixo: 2: 2 e limpar as teclas antigas.

Aqui está uma versão completamente funcional e atômica de um delete curinga implementado em Lua. Ele será executado muito mais rápido que a versão XARGS devido a muito menos rede de rede, e é completamente atômico, bloqueando quaisquer outras solicitações contra o Redis até terminar. Se você deseja excluir atomicamente as chaves no Redis 2.6.0 ou mais, esse é definitivamente o caminho a seguir:

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

Esta é uma versão em funcionamento da ideia de @McDizzle em sua resposta a esta pergunta. Crédito pela idéia 100% vai para ele.

EDITAR: De acordo com o comentário de Kikito abaixo, se você tiver mais chaves para excluir do que a memória livre em seu servidor Redis, você encontrará o "muitos elementos para descompactar" erro. Nesse caso, faça:

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

Como Kikito sugeriu.

Isenção de responsabilidade: a seguinte solução não fornecer atomicidade.

Começando com v2.8 você verdade quer usar o VARREDURA comando em vez de chaves [1]. O script Bash a seguir demonstra a exclusão de chaves por padrão:

#!/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] CHAVES é um comando perigoso que pode resultar em um DOS. A seguir, é apresentada uma cotação de sua página de documentação:

Aviso: Considere as teclas como um comando que deve ser usado apenas em ambientes de produção com extremo cuidado. Pode arruinar o desempenho quando for executado em grandes bancos de dados. Este comando destina -se à depuração e operações especiais, como alterar o layout do seu espaço. Não use as chaves no seu código de aplicativo regular. Se você está procurando uma maneira de encontrar teclas em um subconjunto do seu espaço de chave, considere usar conjuntos.

ATUALIZAR: um revestimento para o mesmo efeito básico -

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

Para aqueles que estavam tendo problemas para analisar outras respostas:

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

Substituir key:*:pattern com seu próprio padrão e entre isso em redis-cli E você está pronto para ir.

Crédito Lisco de: http://redis.io/commands/del

Estou usando o comando abaixo em Redis 3.2.8

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

Você pode obter mais ajuda relacionada à pesquisa de padrões de chaves a partir daqui:- https://redis.io/commands/keys. Use seu padrão conveniente no estilo global, conforme sua exigência como *YOUR_KEY_PREFIX* ou YOUR_KEY_PREFIX?? ou qualquer outro.

E se algum de vocês integrou Biblioteca Redis PHP do que a função abaixo o ajudará.

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);
            }
        }

Obrigada :)

A solução do @McDizle não está funcionando, funciona apenas para uma entrada.

Este funciona para todas as chaves com o mesmo prefixo

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

Observação: Você deve substituir 'prefixo' pelo seu prefixo chave ...

Você também pode usar este comando para excluir as chaves:-

Suponha que existem muitos tipos de chaves em seus redis como-

  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' aqui XYZ é um nome do site, e essas chaves estão relacionadas a produtos e categorias de um site de comércio eletrônico e geradas pelo FPC.

Se você usar este comando como abaixo-

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

OU

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

Ele exclui todas as chaves como 'xyz_category_fpc'(exclua 1, 2 e 3 chaves). Para excluir outros 4, 5 e 6 teclas de número de uso 'xyz_product_fpc'No comando acima.

Se você quiser Exclua tudo dentro Redis, então siga estes comandos-

Com Redis-cli:

  1. Flushdb - Remove os dados do banco de dados atual da sua conexão.
  2. Flushall - Remove dados de todos os bancos de dados.

Por exemplo:- em seu shell:

redis-cli flushall
redis-cli flushdb

Se você tem espaço no nome das teclas, pode usar isso no Bash:

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

@A resposta de Itamar é ótima, mas a análise da resposta não estava funcionando para mim, esp. No caso em que não há chaves encontradas em uma determinada varredura. Uma solução possivelmente mais simples, diretamente do console:

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

Isso também usa o Scan, que é preferível às chaves na produção, mas não é atômico.

Eu só tive o mesmo problema. Eu armazenei dados de sessão para um usuário no formato:

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

Portanto, cada entrada era um par de valores-chave separado. Quando a sessão é destruída, eu queria remover todos os dados da sessão, excluindo chaves com o padrão session:sessionid:* - Mas Redis não tem essa função.

O que eu fiz: armazenar os dados da sessão dentro de um cerquilha. Eu apenas crio um hash com o hash id de session:sessionid E então eu empurro key-x, key-y, key-z nesse hash (a ordem não importava para mim) e se eu não precisar mais daquele hash, eu apenas faço um DEL session:sessionid e todos os dados associados a esse ID de hash se foram. DEL é atômico e acessar dados/gravação de dados para o hash é O (1).

PARA SUA INFORMAÇÃO.

  • Somente usando o Bash e redis-cli
  • não use keys (Isso usa scan)
  • funciona bem em modo de cluster
  • não atômico

Talvez você só precise modificar caracteres de capital.

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

Corra no Bash Prompt

$ ./clear-redis-key.sh key_head_pattern

Eu acho que o que pode ajudá -lo é o Multi/exec/descarte. Enquanto não 100% equivalente a transações, você deve ser capaz de isolar as exclusões de outras atualizações.

É simples implementado via funcionalidade de "Remover ramo" em Cortoredis, basta selecionar o ramo que você deseja remover.

enter image description here

Por favor, use este comando e tente:

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

Uma versão usando a varredura em vez de chaves (conforme recomendado para servidores de produção) e --pipe em vez de xargs.

Eu prefiro o Pipe Over Xargs porque é mais eficiente e funciona quando suas teclas contêm cotações ou outros caracteres especiais que seu shell tente e interpretar. A substituição do Regex neste exemplo envolve a chave em citações duplas e escapa de qualquer cotações duplas dentro.

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

Isso não é uma resposta direta para a pergunta, mas como cheguei aqui ao procurar minhas próprias respostas, vou compartilhar isso aqui.

Se você tiver dezenas ou centenas de milhões de chaves que você deve corresponder, as respostas dadas aqui farão com que Redis não seja responsivo por uma quantidade significativa de tempo (minutos?) E potencialmente falhar devido ao consumo de memória (não se se Entre no meio da sua operação).

A abordagem a seguir é inegavelmente feia, mas não encontrei uma melhor. Atomicidade está fora de questão aqui, neste caso o objetivo principal é manter o Redis para cima e responsivo 100% do tempo. Funcionará perfeitamente se você tiver todas as suas chaves em um dos bancos de dados e não precisar corresponder a nenhum padrão, mas não pode usar http://redis.io/commands/flushdb Por causa de sua natureza bloqueadora.

A ideia é simples: escreva um script que seja executado em um loop e usa operação O (1) como http://redis.io/commands/scan ou http://redis.io/commands/randomkey Para obter chaves, verifica se eles correspondem ao padrão (se precisar) e http://redis.io/commands/del eles um por um.

Se houver uma maneira melhor de fazê -lo, informe -me, atualizarei a resposta.

Exemplo de implementação com RandomKey em Ruby, como uma tarefa de rake, um substituto sem bloqueio de algo como 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

Eu tentei a maioria dos métodos mencionados acima, mas eles não funcionaram para mim, depois de algumas pesquisas, encontrei esses pontos:

  • Se você tiver mais de um dB no redis, determine o banco de dados usando -n [number]
  • Se você tem algumas chaves usadas del Mas se houver milhares ou milhões de chaves, é melhor usar unlink Porque A desinpedência não é bloqueada Enquanto Del está bloqueando, para mais informações, visite esta página Desligue vs del
  • também keys são como del e estão bloqueando

Então eu usei este código para excluir as chaves por padrão:

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

Apoio todas as respostas relacionadas a ter alguma ferramenta ou executar a expressão da Lua.

Mais uma opção do meu lado:

Em nossos bancos de dados de produção e pré-produção, existem milhares de chaves. De tempos em tempos, precisamos excluir algumas chaves (por alguma máscara), modificar por alguns critérios etc. É claro que não há como fazê -lo manualmente da CLI, especialmente tendo sharding (512 DBs lógicos em cada físico).

Para esse fim, escrevo uma ferramenta de cliente Java que faz tudo isso. No caso de exclusão de chaves, a concessionária pode ser muito simples, apenas uma classe lá:

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();
    }

}

Abaixo o comando funcionou para mim.

redis-cli -h redis_host_url KEYS "*abcd*" | xargs redis-cli -h redis_host_url DEL

Anúncio de agora, você pode usar um cliente Redis e executar a primeira varredura (suporta a correspondência de padrões) e, em seguida, delineado cada tecla individualmente.

No entanto, há um problema no Redis Github oficial para criar um padrão de correspondência aqui, vá mostrar um pouco de amor se você achar útil!

Delete em massa atômica do pobre homem?

Talvez você possa definir todos para expirar o mesmo segundo - como alguns minutos no futuro - e depois esperar até aquele momento e vê -los todos "se autodestruir" ao mesmo tempo.

Mas não tenho muita certeza de quão atômico isso seria.

O próprio REDISTEMPLATE da mola fornece a funcionalidade. O RedissoClient na versão mais recente depreciou a funcionalidade "DeleteBypattern".

Set<String> keys = redisTemplate.keys("geotag|*");
redisTemplate.delete(keys);
Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top