Cómo teclas de borrado atómicamente coinciden con un patrón usando Redis

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

  •  25-09-2019
  •  | 
  •  

Pregunta

En mi Redis DB Tengo un número de hashes prefix:<numeric_id>.

A veces quiero purgar a todos atómicamente. ¿Cómo puedo hacer esto sin el uso de algún mecanismo de bloqueo distribuido?

¿Fue útil?

Solución

A partir de Redis 2.6.0, puede ejecutar secuencias de comandos LUA, que se ejecutan atómicamente. Nunca he escrito una, pero creo que sería algo como esto

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

Vea la EVAL documentación .

Otros consejos

Ejecutar en bash:

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

Actualizar

Ok, i entendido. ¿Qué hay de esta manera: tienda actual prefijo incremento adicional y añadirlo a todas sus claves. Por ejemplo:

Usted tiene los valores de esta manera:

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

Cuando necesite datos de purga, se cambia prefix_actuall primero (por ejemplo conjunto prefix_prefix_actuall = 3), por lo que su aplicación va a escribir nuevos datos en combinaciones prefijo: 3: 1 y el prefijo: 3: 2. A continuación, puede tomar con seguridad los viejos valores de prefijo: 2: 1 y el prefijo: 2:. 2 y purgar viejas llaves

Aquí hay una versión completamente de trabajo y atómico de un comodín delete implementado en Lua. Se va a correr mucho más rápido que la versión xargs debido al mucho menos a la red de ida y vuelta, y es completamente atómica, bloqueando cualquier otra petición contra Redis hasta que termine. Si desea teclas atómicamente eliminación en Redis 2.6.0 o superior, este es definitivamente el camino 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 es una versión de trabajo de la idea de @ mcdizzle en su respuesta a esta pregunta. El crédito para la idea 100% va a él.

EDIT: Comentario de Per Kikito a continuación, si usted tiene más teclas para borrar de la memoria libre en su servidor Redis, se encontrará con la "demasiados elementos para desempaquetar" error . En ese caso, hacer:

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

Como Kikito sugirió.

exención de responsabilidad:. La siguiente solución no proporcionar atomicidad

A partir de v2.8 que realmente que desee utilizar el comando SCAN en lugar de las teclas [1 ]. La siguiente secuencia de comandos Bash demuestra eliminación de claves por patrón:

#!/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] TECLAS es un comando peligroso que potencialmente puede resultar en una DoS. La siguiente es una cita de su página de documentación:

  

Advertencia: CLAVES consideran como un comando que sólo debe utilizarse en entornos de producción con extremo cuidado. Se puede arruinar el rendimiento cuando se ejecuta en contra de grandes bases de datos. Este comando está pensado para las operaciones de depuración y especiales, tales como cambiar su diseño de espacio de claves. No utilice claves en el código de aplicación regular. Si usted está buscando una manera de encontrar las llaves en un subconjunto de su espacio de claves, considerar el uso de conjuntos.

UPDATE: a un trazador de líneas para el mismo efecto básico -

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

Para aquellos que estaban teniendo problemas para analizar otras respuestas:

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

Reemplazar key:*:pattern con su propio patrón y entrar en esto en redis-cli y que son buenos para ir.

Lisco de crédito a partir de: http://redis.io/commands/del

Estoy utilizando a continuación mando en Redis 3.2.8

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

Se puede obtener más ayuda relacionada con las teclas de búsqueda de patrones de aquí: - https://redis.io/commands/ llaves . Utilice su cómoda de estilo patrón glob como por su exigencia como *YOUR_KEY_PREFIX* o YOUR_KEY_PREFIX?? o cualquier otro.

Y si alguno de ustedes han integrado biblioteca Redis PHP que por debajo de la función le ayudará.

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

Gracias:)

solución

@ de mcdizle no funciona funciona sólo para una entrada.

Esta funciona para todas las llaves con el mismo prefijo

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

Nota: Debe reemplazar 'prefijo' con su tecla prefijo ...

También puede utilizar este comando para eliminar las claves: -

Supongamos que hay muchos tipos de llaves en sus 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'

El ex ' xyz_category_fpc ' aquí XYZ es un nombre del sitio , y estas claves están relacionadas con los productos y categorías de un sitio de comercio electrónico y generada por FPC.

Si se utiliza este comando de la siguiente manera -

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

o

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

Se elimina todas las claves como ' xyz_category_fpc ' (borrar 1, 2 y 3 llaves). Para otras teclas 4, 5 y 6 Borrar número usan ' xyz_product_fpc ' en el comando anterior.

Si quieres Eliminar todo en Redis , a continuación, siga estos comandos -

Con Redis-CLI:

  1. FLUSHDB -. Elimina los datos de la base de datos actual de su conexión
  2. flushall -. Elimina los datos de bases de datos TODOS

Por ejemplo: - en su shell:

redis-cli flushall
redis-cli flushdb

Si tiene espacio en el nombre de las teclas, puede utilizar esto en bash:

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

@ de Itamar es grande, pero el análisis de la respuesta no estaba trabajando para mí, esp. en el caso en que no hay teclas que se encuentran en un análisis dado. Una solución más simple posible, directamente desde la consola:

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

Esto también utiliza SCAN, que es preferible a las teclas en la producción, pero no es atómica.

Sólo tenía el mismo problema. Almacené datos de la sesión de un usuario en el formato:

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

Por lo tanto, cada entrada era un par clave-valor separado. Cuando la sesión se destruye, quería eliminar todos los datos de la sesión por la eliminación de claves con la session:sessionid:* patrón -. Redis, pero no tiene tal función

Lo que hice: almacenar los datos de la sesión dentro de un de hash . Me acabo de crear un hash con el ID de hash de session:sessionid y luego empujo key-x, key-y, key-z en esa almohadilla (para que no me importaba) y si no necesitas ese hash de más Me acaba de hacer un DEL session:sessionid y todos los datos asociados a ese Identificación de hash se ha ido. DEL es atómico y acceder a datos / escritura de datos en el hash es O (1).

Para su información.

  • Sólo usando bash y redis-cli
  • no usar keys (esto usos scan)
  • funciona bien en el modo de clúster
  • no atómica

Tal vez sólo necesita 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

Ejecutar en el indicador bash

$ ./clear-redis-key.sh key_head_pattern

creo que lo que podría ayudar es la MULTI / EXEC / DESCARTAR . Aunque no es 100% equivalente de transacciones , debe ser capaz de aislar las eliminaciones de otras actualizaciones.

Es simple implementado a través de la funcionalidad "Quitar rama" en FastoRedis , sólo tiene que seleccionar la rama que desea eliminar.

introducir descripción de la imagen aquí

Por favor, use este comando y tratar:

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

Una versión utilizando SCAN en lugar de teclas (como se recomienda para los servidores de producción) y --pipe en lugar de xargs.

Yo prefiero tubería sobre xargs porque es más eficiente y funciona cuando sus claves contienen comillas u otros caracteres especiales que su cáscara con intente interpretar. La sustitución de expresiones regulares en este ejemplo envuelve la clave entre comillas dobles, y se escapa ningún comillas dobles en el interior.

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

Esto no es una respuesta directa a la pregunta, pero desde que estoy aquí en la búsqueda de mis propias respuestas, voy a compartir esto aquí.

Si usted tiene decenas o cientos de millones de llaves que tienen que coincidir, las respuestas que se dan aquí hará que Redis a ser no responde a cantidad significativa de tiempo (minutos?), Y potencialmente fallar debido al consumo de memoria (asegúrese, fondo Guardar entrará en funcionamiento en el medio de su operación).

El siguiente enfoque es sin lugar a dudas feo, pero que no encontrar una mejor. La atomicidad está fuera de cuestión aquí, en este caso objetivo principal es mantener Redis y responde al 100% de las veces. Que funcionará perfectamente si tiene todas las llaves en una de las bases de datos y no es necesario para adaptarse a cualquier patrón, pero no puede utilizar http://redis.io/commands/FLUSHDB debido a que está bloqueando la naturaleza.

La idea es simple: escribir un script que se ejecuta en un bucle y usos O (1) operación como http: // redis.io/commands/SCAN o http://redis.io/commands/RANDOMKEY para obtener las claves, comprueba si coinciden con el patrón (si lo necesita) y http://redis.io/commands/ DEL ellos uno por uno.

Si hay una mejor manera de hacerlo, por favor hágamelo saber, voy a actualizar la respuesta.

Ejemplo aplicación con randomkey en Ruby, como una tarea rastrillo, no un sustituto de algo así como el bloqueo 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

Me trató la mayor parte de los métodos mencionados anteriormente, pero que no funcionó para mí, después de algunas búsquedas encontré estos puntos:

  • Si usted tiene más de una db en Redis debe determinar la base de datos utilizando -n [number]
  • Si usted tiene un par de claves utilizan del pero si hay miles o millones de claves que es mejor usar unlink porque Desvincular es no bloqueante , mientras que del es el bloqueo, para obtener más información, visite esta página < a href = "https://stackoverflow.com/questions/45818371/is-the-unlink-command-always-better-than-del-command"> de desvinculación del vs
  • también keys son como del bloqueo y está

por lo que utiliza este código para llaves de eliminación por el patrón:

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

Apoyo todas las respuestas relacionadas con tener alguna herramienta o ejecutar expresión Lua.

Una de las opciones más de mi lado:

En nuestra base de datos de producción y pre-producción, hay miles de llaves. De vez en cuando hay que eliminar algunas teclas (por alguna máscara), modificar algunos criterios, etc. Por supuesto, no hay manera de hacerlo manualmente desde la CLI, sobre todo teniendo sharding (512 dbs lógicos en cada física).

En esta herramienta cliente Java de escritura objetivo que hace que todo este trabajo. En el caso de claves borren La utilidad puede ser muy simple, sólo una clase existe:

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

}

A continuación comando funcionó para mí.

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

Anuncio de ahora, se puede utilizar un cliente Redis y realizar primeros soportes de SCAN (coincidencia de patrones) y luego DEL cada tecla individual.

Sin embargo, hay un problema en github oficial Redis para crear un golpeteo de igualación del aquí , ir mostrar un poco de amor, si lo encuentra útil!

masa atómica de eliminación del pobre?

Tal vez usted podría establecer que todos ellos EXPIREAT el mismo segundo - al igual que un par de minutos en el futuro -. Y luego esperar hasta que el tiempo y los ven todos "autodestrucción", al mismo tiempo

pero no estoy muy seguro de cómo atómica que habría.

sí primavera RedisTemplate proporciona la funcionalidad. RedissonClient en la última versión ha desaprobado la funcionalidad "deleteByPattern".

Set<String> keys = redisTemplate.keys("geotag|*");
redisTemplate.delete(keys);
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top