Comment supprimer atomiquement les clés correspondant à un modèle à l'aide de Redis

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

  •  25-09-2019
  •  | 
  •  

Question

Dans ma base de données Redis, j'ai un certain nombre de prefix:<numeric_id> hachages.

Parfois, j'ai envie de tous les purger de manière atomique.Comment puis-je procéder sans utiliser de mécanisme de verrouillage distribué ?

Était-ce utile?

La solution

A partir de Redis 2.6.0, vous pouvez exécuter des scripts LUA, qui exécutent atomiquement. Je ne l'ai jamais écrit, mais je pense qu'il ressemblerait à quelque chose comme ceci

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

Voir la documentation EVAL .

Autres conseils

Exécuter en bash:

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

UPDATE

Ok, i compris. Qu'en est-il de cette façon: stocker le préfixe différentiel supplémentaire courant et l'ajouter à toutes vos clés. Par exemple:

Vous avez des valeurs comme ceci:

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

Lorsque vous devez purger les données, vous changez prefix_actuall premier (par exemple de prefix_prefix_actuall = 3), de sorte que votre application écrire de nouvelles données préfixe clés: 3: 1 et préfixe: 3: 2. Ensuite, vous pouvez prendre en toute sécurité les anciennes valeurs de préfixe: 2: 1 et le préfixe: 2:. 2 et purger les anciennes clés

Voici une version entièrement fonctionnelle et atomique d'une suppression générique implémentée dans Lua.Il fonctionnera beaucoup plus rapidement que la version xargs en raison de beaucoup moins d'allers-retours réseau, et il est complètement atomique, bloquant toute autre requête contre Redis jusqu'à ce qu'il se termine.Si vous souhaitez supprimer atomiquement des clés sur Redis 2.6.0 ou version ultérieure, voici la voie à suivre :

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

Il s'agit d'une version fonctionnelle de l'idée de @mcdizzle dans sa réponse à cette question.Le mérite de l’idée lui revient à 100 %.

MODIFIER: Selon le commentaire de Kikito ci-dessous, si vous avez plus de clés à supprimer que de mémoire libre sur votre serveur Redis, vous rencontrerez le Erreur "trop ​​d'éléments à décompresser".Dans ce cas, faites :

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

Comme l'a suggéré Kikito.

Disclaimer:. La solution suivante ne pas fournissent atomicité

A partir de v2.8 vous vraiment veulent utiliser la commande SCAN au lieu de touches [1 ]. Le script Bash suivant illustre la suppression des clés par modèle:

#!/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] TOUCHES est une commande dangereuse qui peut potentiellement entraîner un déni de service. Ce qui suit est une citation de la page de documentation:

  

Attention: considérer TOUCHES comme une commande qui ne devrait être utilisé dans des environnements de production avec un soin extrême. Il peut ruiner la performance lorsqu'elle est exécutée sur de grandes bases de données. Cette commande est destinée pour le débogage et les opérations spéciales, telles que la modification de votre mise en page de keyspace. Ne pas utiliser les touches dans votre code d'application régulière. Si vous êtes à la recherche d'un moyen de trouver les clés dans un sous-ensemble de votre keyspace, pensez à utiliser des ensembles.

UPDATE: une seule ligne pour le même effet de base -

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

Pour ceux qui ont de la difficulté à l'analyse syntaxique autres réponses:

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

Remplacer key:*:pattern avec votre propre modèle et entrez dans ce redis-cli et vous êtes bon pour aller.

LISCO de crédit à partir de: http://redis.io/commands/del

J'utilise ci-dessous commande dans Redis 3.2.8

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

Vous pouvez obtenir plus d'aide en rapport avec les clés de recherche de modèle à partir d'ici: - https://redis.io/commands/ clés . Utilisez votre modèle pratique glob-style selon vos besoins comme *YOUR_KEY_PREFIX* ou YOUR_KEY_PREFIX?? ou tout autre.

Et si l'un d'entre vous ont intégré Bibliothèque Redis PHP que ci-dessous fonction vous aider.

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

Merci:)

@ solution de mcdizle ne fonctionne pas, il ne fonctionne que pour une seule entrée.

Celui-ci fonctionne pour toutes les clés avec le même préfixe

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

Remarque: Vous devez remplacer 'préfixe' avec préfixe clé ...

Vous pouvez également utiliser cette commande pour supprimer les clés: -

Supposons qu'il existe plusieurs types de clés dans vos Redis comme -

  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 ici xyz est un sitename , et ces clés sont liées aux produits et catégories d'un site E-Commerce et généré par FPC.

Si vous utilisez cette commande comme ci-dessous -

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

ou

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

Il supprime toutes les clés comme ' xyz_category_fpc (supprimer 1, 2 et 3 clés). Pour supprimer d'autres 4, 5 et 6 touches numériques utilisent ' xyz_product_fpc dans la commande ci-dessus.

Si vous voulez Supprimer tout Redis , puis suivez ces commandes -

Avec Redis-cli:

  1. FLUSHDB - Supprime les données de base de données en cours de la connexion
  2. .
  3. FLUSHALL -. Supprime les données de toutes les bases

Par exemple: - dans votre shell:

redis-cli flushall
redis-cli flushdb

Si vous avez l'espace au nom des clés, vous pouvez l'utiliser dans bash:

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

@ La réponse de Itamar est grande, mais l'analyse syntaxique de la réponse ne fonctionnait pas pour moi, surtout. dans le cas où il n'y a pas des clés trouvées dans une analyse donnée. Une solution peut-être plus simple, directement à partir de la console:

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

Il utilise également SCAN, ce qui est préférable à des clés de la production, mais pas atomique.

Je viens d'avoir le même problème. J'ai emmagasiné les données de session pour un utilisateur dans le format suivant:

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

Ainsi, chaque entrée était une paire séparée clé-valeur. Lorsque la session est détruit, je voulais supprimer toutes les données de session en supprimant les clés avec le motif session:sessionid:* -. Mais Redis ne possède pas une telle fonction

Ce que je l'ai fait: stocker les données de session dans un de hachage. Je viens de créer un hachage avec l'identifiant de hachage de session:sessionid puis je pousse key-x, key-y, key-z dans ce hachage (ordre n'a pas d'importance pour moi) et si je ne ai besoin que hachage plus je fais juste un DEL session:sessionid et toutes les données associées à cette id hachage est disparu. DEL est atomique et accéder à des données / écrire des données sur la valeur de hachage est O (1).

Pour votre information.

  • uniquement en utilisant bash et redis-cli
  • ne pas utiliser keys (celui-ci utilise scan)
  • fonctionne bien dans mode cluster
  • pas atomique

Peut-être que vous ne devez modifier les caractères majuscules.

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

Exécuter à l'invite bash

$ ./clear-redis-key.sh key_head_pattern

Je pense que ce qui pourrait vous aider est de défausse. Sans 100% équivalent des transactions , vous devrait être en mesure d'isoler les suppressions d'autres mises à jour.

Il est simple mise en œuvre via la fonctionnalité « Supprimer branche » dans FastoRedis , il suffit de sélectionner la branche que vous souhaitez supprimer.

S'il vous plaît utiliser cette commande et essayez:

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

Une version utilisant SCAN plutôt que de touches (comme recommandé pour les serveurs de production) et --pipe plutôt que xargs.

Je préfère tuyau sur xargs car il est plus efficace et fonctionne lorsque vos clés contiennent des citations ou d'autres caractères spéciaux que votre shell avec essayer et à interpréter. La substitution regex dans cet exemple enveloppe la clé entre guillemets, et échappe à toute guillemets à l'intérieur.

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

Ce n'est pas de réponse directe à la question, mais depuis que je suis arrivé ici en recherchant mes réponses, je vais partager ce ici.

Si vous avez des dizaines ou des centaines de millions de clés que vous devez faire correspondre, les réponses données ici causera Redis être non réactif pour beaucoup de temps (minutes?), Et planter potentiellement en raison de la consommation de mémoire (assurez-vous, fond sauver donnera le coup dans au milieu de votre opération).

L'approche suivante est indéniablement laid, mais je ne trouve pas une meilleure. Atomicité est hors de question ici, dans ce cas, objectif principal est de maintenir Redis et répondre à 100% du temps. Il fonctionnera parfaitement si vous avez toutes vos clés dans l'une des bases de données et vous n'avez pas besoin de faire correspondre tout motif, mais ne peut pas utiliser http://redis.io/commands/FLUSHDB en raison de sa nature de blocage.

L'idée est simple: écrire un script qui tourne en boucle et utilise opération O (1) comme http: // redis.io/commands/SCAN ou http://redis.io/commands/RANDOMKEY pour obtenir les clés, vérifie si elles correspondent au modèle (si vous en avez besoin) et http://redis.io/commands/ DEL les un par un.

S'il y a une meilleure façon de le faire, s'il vous plaît laissez-moi savoir, je vais mettre à jour la réponse.

mise en œuvre Exemple avec randomkey en Ruby, comme une tâche de râteau, un substitut non de blocage de quelque chose comme 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

J'ai essayé la plupart des méthodes mentionnées ci-dessus, mais ils ne travaillaient pas pour moi, après quelques recherches, je trouve ces points:

  • si vous avez plus d'un db sur Redis vous devez déterminer la base de données en utilisant -n [number]
  • si vous avez quelques clés utilisent del mais s'il y a des milliers ou des millions de clés, il est préférable d'utiliser unlink parce que unlink est non bloquant tout del bloque, pour plus d'informations, visitez cette page < a href = "https://stackoverflow.com/questions/45818371/is-the-unlink-command-always-better-than-del-command"> unlink vs del
  • aussi keys sont comme del et bloque

donc j'ai utilisé ce code pour supprimer des clés par modèle:

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

Je soutiens toutes les réponses liées à avoir un certain outil ou exécuter l'expression Lua.

Une option plus de mon côté:

Dans notre production et les bases de données de pré-production, il y a des milliers de clés. De temps en temps, nous devons supprimer certaines touches (par un certain masque), modifier certains critères, etc. Bien sûr, il n'y a aucun moyen de le faire manuellement à partir de la CLI, en particulier ayant sharding (512 dbs logiques dans chaque physique).

A cet effet, je vous écris outil client java qui fait tout ce travail. En cas de suppression des clés de l'utilitaire peut être très simple, une seule classe, il:

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

}

Ci-dessous commande a fonctionné pour moi.

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

Annonce maintenant, vous pouvez utiliser un client Redis et effectuer les premiers SCAN (prend en charge la recherche de motifs) puis DEL chaque touche individuellement.

Cependant, il y a un problème sur GitHub officiel de Redis pour créer un bagout de correspondance-del ici , allez montrer un peu d'amour si vous le trouvez utile!

pauvre de masse atomique supprimer?

vous pourriez peut-être les mettre tous à la même seconde EXPIREAT - comme quelques minutes à l'avenir -. Puis attendre que le temps et les voir « autodétruire » en même temps

mais je ne suis pas vraiment sûr de savoir comment atomique qui serait.

Spring RedisTemplate lui-même fournit la fonctionnalité. RedissonClient dans la dernière version a désapprouvé la fonctionnalité "deleteByPattern".

Set<String> keys = redisTemplate.keys("geotag|*");
redisTemplate.delete(keys);
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top