No meu Redis DB, tenho vários prefix:<numeric_id>
hashes.
Às vezes eu quero limpar todos eles atomicamente. Como faço isso sem usar algum mecanismo de bloqueio distribuído?
No meu Redis DB, tenho vários prefix:<numeric_id>
hashes.
Às vezes eu quero limpar todos eles atomicamente. Como faço isso sem usar algum mecanismo de bloqueio distribuído?
Respostas:
A partir do redis 2.6.0, você pode executar scripts lua, que são executados atomicamente. Eu nunca escrevi um, mas acho que seria algo como isto
EVAL "return redis.call('del', unpack(redis.call('keys', ARGV[1])))" 0 prefix:[YOUR_PREFIX e.g delete_me_*]
Aviso : Como o documento Redis diz, por causa dos indicadores de desempenho, o
keys
comando não deve ser usado para operações regulares na produção, este comando é destinado à depuração e operações especiais. consulte Mais informação
Veja a documentação do EVAL .
EVAL "local keys = redis.call('keys', ARGV[1]) \n for i=1,#keys,5000 do \n redis.call('del', unpack(keys, i, math.min(i+4999, #keys))) \n end \n return keys" 0 prefix:*
del prefix:*
ser uma operação fundamental: /
EVAL "return redis.call('del', 'defaultKey', unpack(redis.call('keys', ARGV[1])))" 0 prefix:*
Executar no bash:
redis-cli KEYS "prefix:*" | xargs redis-cli DEL
ATUALIZAR
OK eu entendi. E quanto a isso: armazene o prefixo incremental adicional atual e adicione-o a todas as suas chaves. Por exemplo:
Você tem valores como este:
prefix_prefix_actuall = 2
prefix:2:1 = 4
prefix:2:2 = 10
Quando você precisar limpar os dados, altere o prefixo_actuall primeiro (por exemplo, defina prefix_prefix_actuall = 3), para que seu aplicativo grave novos dados nos prefixos de chaves: 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 chaves antigas.
redis-cli KEYS "prefix:*" | xargs --delim='\n' redis-cli DEL
redis-cli -n 3 KEYS "prefix:*" | xargs redis-cli -n 3 DEL
Aqui está uma versão completamente funcional e atômica de uma exclusão curinga implementada em Lua. Ele rodará muito mais rápido que a versão xargs devido a muito menos retorno e retorno da rede, e é completamente atômico, bloqueando outras solicitações contra o redis até que seja concluído. Se você deseja excluir atomicamente chaves no Redis 2.6.0 ou superior, este é 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 funcional da idéia de @ mcdizzle em sua resposta a esta pergunta. O crédito pela ideia 100% vai para ele.
EDIT: Pelo comentário de Kikito abaixo, se você tiver mais chaves para excluir do que memória livre no servidor Redis, encontrará o erro "muitos elementos para descompactar" . Nesse caso, faça:
for _,k in ipairs(redis.call('keys', ARGV[1])) do
redis.call('del', k)
end
Como Kikito sugeriu.
for _,k in ipairs(redis.call('keys', KEYS[1])) do redis.call('del', k) end
unpack
transforma uma tabela em uma "lista de variáveis independentes" (outras línguas chamam isso explode
), mas o número máximo não depende da memória do sistema; é fixado em lua através da LUAI_MAXSTACK
constante. No Lua 5.1 e LuaJIT é 8000 e no Lua 5.2 é 100000. A opção for loop é recomendada IMO.
EVAL
pois não especifica antecipadamente as chaves nas quais irá operar. Ele deve funcionar em uma única instância, mas não espere que funcione com o Redis Cluster.
Isenção de responsabilidade: a solução a seguir não fornece atomicidade.
A partir da v2.8, você realmente deseja usar o comando DIGITALIZAR em vez de KEYS [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] KEYS é um comando perigoso que pode resultar potencialmente em um DoS. A seguir, uma citação de sua página de documentação:
Aviso: considere KEYS como um comando que deve ser usado apenas em ambientes de produção com extremo cuidado. Isso pode prejudicar o desempenho quando executado em bancos de dados grandes. Este comando destina-se à depuração e operações especiais, como alterar o layout do espaço da chave. Não use KEYS no seu código de aplicativo normal. Se você estiver procurando uma maneira de encontrar chaves em um subconjunto do seu espaço de chaves, considere usar conjuntos.
UPDATE: um liner para o mesmo efeito básico -
$ redis-cli --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli DEL
-n 1
a cada redis-cli
invocação:redis-cli -n 1 --scan --pattern "*:foo:bar:*" | xargs -L 100 redis-cli -n 1 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
Substitua key:*:pattern
por seu próprio padrão e insira isso redis-cli
e você estará pronto.
Crédito lisco de: http://redis.io/commands/del
Estou usando o comando abaixo no 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 aqui: - https://redis.io/commands/keys . Use o seu padrão de estilo glob conveniente como por sua exigência como *YOUR_KEY_PREFIX*
ou YOUR_KEY_PREFIX??
ou qualquer outro.
E se algum de vocês tiver integrado a biblioteca Redis PHP, 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);
}
}
Obrigado :)
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*
Nota: Você deve substituir 'prefixo' pelo seu prefixo de chave ...
Você também pode usar este comando para excluir as chaves: -
Suponha que haja muitos tipos de chaves nos seus redis, como
Ex- ' xyz_category_fpc ' aqui xyz é um nome de 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
Exclui todas as chaves como ' xyz_category_fpc ' (exclua as teclas 1, 2 e 3). Para excluir outras teclas numéricas 4, 5 e 6, use ' xyz_product_fpc ' no comando acima.
Se você deseja excluir tudo no Redis , siga estes comandos
Com redis-cli:
Por exemplo: - no seu shell:
redis-cli flushall
redis-cli flushdb
redis-cli del
não é atômica.
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 SCAN, que é preferível a KEYS na produção, mas não é atômico.
Eu apenas tive o mesmo problema. Eu armazenei os dados da 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 o redis não possui essa função.
O que eu fiz: armazene os dados da sessão em um hash . Eu só criar um hash com a ID de hash session:sessionid
e então eu empurrar key-x
, key-y
, key-z
em que de hash (ordem não importa para mim) e se eu não preciso esse hash mais Eu só faço um DEL session:sessionid
e todos os dados associados a esse ID de hash é ido. DEL
é atômico e o acesso aos dados / gravação de dados no hash é O (1).
Eu acho que o que pode ajudá-lo é o MULTI / EXEC / DISCARD . Embora não seja 100% equivalente a transações , você poderá isolar as exclusões de outras atualizações.
PARA SUA INFORMAÇÃO.
redis-cli
keys
(isso usa scan
)Talvez você precise modificar apenas caracteres maiúsculos.
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
Executar no prompt do bash
$ ./clear-redis-key.sh key_head_pattern
Outras respostas podem não funcionar se sua chave contiver caracteres especiais - Guide$CLASSMETADATA][1]
por exemplo. A quebra de cada chave entre aspas garantirá que elas sejam excluídas corretamente:
redis-cli --scan --pattern sf_* | awk '{print $1}' | sed "s/^/'/;s/$/'/" | xargs redis-cli del
Uma versão que usa SCAN em vez de KEYS (como recomendado para servidores de produção) e em --pipe
vez de xargs.
Prefiro canalizar sobre xargs porque é mais eficiente e funciona quando suas chaves contêm aspas ou outros caracteres especiais que seu shell tenta e interpreta. A substituição de expressão regular neste exemplo envolve a chave entre aspas duplas e escapa as aspas 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
Esta não é uma resposta direta à 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 com as quais corresponder, as respostas fornecidas aqui farão com que os Redis não respondam por um período significativo de tempo (minutos?) E potencialmente falhem devido ao consumo de memória (certifique-se de que o salvamento em segundo plano será chute no meio de sua operação).
A abordagem a seguir é inegavelmente feia, mas não encontrei uma melhor. A atomicidade está fora de questão aqui, neste caso, o principal objetivo é manter o Redis ativo 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 puder usar http://redis.io/commands/FLUSHDB por causa de sua natureza bloqueadora.
A idéia é simples: escreva um script que execute em loop e use a operação O (1) como http://redis.io/commands/SCAN ou http://redis.io/commands/RANDOMKEY para obter chaves, verifique se elas corresponda ao padrão (se necessário) e http://redis.io/commands/DEL, um por um.
Se houver uma maneira melhor de fazer isso, entre em contato, atualizarei a resposta.
Exemplo de implementação com randomkey no Ruby, como uma tarefa rake, um substituto não-bloqueador 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
É simples implementado através da funcionalidade "Remover ramo" no FastoRedis , basta selecionar o ramo que você deseja remover.
Por favor, use este comando e tente:
redis-cli --raw keys "$PATTERN" | xargs redis-cli del
Eu tentei a maioria dos métodos mencionados acima, mas eles não funcionaram para mim, depois de algumas pesquisas, encontrei estes pontos:
-n [number]
del
mas se existem milhares ou milhões de chaves, é melhor usar unlink
porque o desvincular não está bloqueando enquanto o del está bloqueando, para obter mais informações, visite esta página unlink vs delkeys
são como del e está bloqueandoentão usei esse código para excluir chaves por padrão:
redis-cli -n 2 --scan --pattern '[your pattern]' | xargs redis-cli -n 2 unlink
Agora, você pode usar um cliente redis e executar a primeira DIGITALIZAÇÃO (suporta a correspondência de padrões) e, em seguida, DEL cada tecla individualmente.
No entanto, existe um problema no redis github oficial para criar um delineador de correspondência de patter aqui , mostre-lhe um pouco de amor se achar útil!
Eu apoio todas as respostas relacionadas a ter alguma ferramenta ou executar a expressão Lua.
Mais uma opção do meu lado:
Nos nossos bancos de dados de produção e pré-produção, existem milhares de chaves. Ocasionalmente, precisamos excluir algumas chaves (por alguma máscara), modificar por alguns critérios etc. É claro que não há como fazê-lo manualmente a partir da CLI, principalmente com sharding (512 dbs lógicos em cada físico).
Para esse propósito, escrevo a ferramenta cliente java que faz todo esse trabalho. No caso de exclusão de chaves, o utilitário pode ser muito simples, apenas uma classe:
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();
}
}
O comando abaixo funcionou para mim.
redis-cli -h redis_host_url KEYS "*abcd*" | xargs redis-cli -h redis_host_url DEL
O Spring RedisTemplate propriamente dito fornece a funcionalidade. O RedissonClient na versão mais recente descontinuou a funcionalidade "deleteByPattern".
Set<String> keys = redisTemplate.keys("geotag|*");
redisTemplate.delete(keys);
keys
e delete
invocações.