Redis Python: cómo eliminar todas las claves de acuerdo con un patrón específico en Python, sin iteración de python

Estoy escribiendo un comando de administración de django para manejar parte de nuestro almacenamiento en caché de redis. Básicamente, debo elegir todas las teclas, que confirman un determinado patrón (por ejemplo: “prefijo: *”) y eliminarlas.

Sé que puedo usar el cli para hacer eso:

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

Pero necesito hacer esto desde dentro de la aplicación. Así que necesito usar el enlace de python (estoy usando py-redis). He intentado introducir una lista en eliminar, pero falla:

 from common.redis_client import get_redis_client cache = get_redis_client() x = cache.keys('prefix:*') x == ['prefix:key1','prefix:key2'] # True 

# Y ahora

 cache.delete(x) 

# devuelve 0. no se elimina nada

    Sé que puedo iterar sobre x:

     for key in x: cache.delete(key) 

    Pero eso sería perder la velocidad increíble y el mal uso de sus capacidades. ¿Hay una solución pythonica con py-redis, sin iteración y / o el cli?

    ¡Gracias!

    creo que el

      for key in x: cache.delete(key) 

    Es bastante bueno y conciso. delete realmente quiere una tecla a la vez, así que tienes que hacer un bucle.

    De lo contrario, esta pregunta y respuesta anteriores lo llevarán a una solución basada en lua.

    Use los iteradores SCAN: https://pypi.python.org/pypi/redis

     for key in r.scan_iter("prefix:*"): r.delete(key) 

    De la documentacion

     delete(*names) Delete one or more keys specified by names 

    Esto solo quiere que se elimine un argumento por clave y luego le dirá cuántos de ellos se encontraron y eliminaron.

    En el caso de su código anterior, creo que solo puede hacer:

      redis.delete(*x) 

    Pero debo admitir que soy nuevo en Python y acabo de hacer:

      deleted_count = redis.delete('key1', 'key2') 

    Aquí hay un ejemplo completo de trabajo usando py-redis :

     from redis import StrictRedis cache = StrictRedis() def clear_ns(ns): """ Clears a namespace :param ns: str, namespace ie your:prefix :return: int, cleared keys """ count = 0 ns_keys = ns + '*' for key in cache.scan_iter(ns_keys): cache.delete(key) count += 1 return count 

    También puede hacer scan_iter para obtener todas las claves en la memoria, y luego pasar todas las claves para delete para una eliminación masiva, pero puede necesitar una buena parte de la memoria para espacios de nombres más grandes. Así que probablemente lo mejor es ejecutar una delete para cada clave.

    ¡Aclamaciones!

    ACTUALIZAR:

    Desde que escribí la respuesta, comencé a usar la función de canalización de redis para enviar todos los comandos en una solicitud y evitar la latencia de la red:

     from redis import StrictRedis cache = StrictRedis() def clear_cache_ns(ns): """ Clears a namespace in redis cache. This may be very time consuming. :param ns: str, namespace ie your:prefix* :return: int, num cleared keys """ count = 0 pipe = cache.pipeline() for key in cache.scan_iter(ns_keys): pipe.delete(key) count += 1 pipe.execute() return count 

    ACTUALIZACIÓN2 (Mejor desempeño):

    Si usa scan lugar de scan_iter , puede controlar el tamaño del fragmento y recorrer el cursor utilizando su propia lógica. Esto también parece ser mucho más rápido, especialmente cuando se trata de muchas claves. Si agrega la canalización a esto, obtendrá un poco de aumento de rendimiento, 10-25% según el tamaño del fragmento, al costo del uso de la memoria, ya que no enviará el comando de ejecución a Redis hasta que se genere todo. Así que me quedé con la exploración:

     from redis import StrictRedis cache = StrictRedis() CHUNK_SIZE = 5000 def clear_ns(ns): """ Clears a namespace :param ns: str, namespace ie your:prefix :return: int, cleared keys """ cursor = '0' ns_keys = ns + '*' while cursor != 0:: cursor, keys = cache.scan(cursor=cursor, match=ns_keys, count=CHUNK_SIZE) if keys: cache.delete(*keys) return True 

    Aquí hay algunos puntos de referencia:

    5k trozos utilizando un grupo ocupado de Redis: Done removing using scan in 4.49929285049 Done removing using scan_iter in 98.4856731892 Done removing using scan_iter & pipe in 66.8833789825 Done removing using scan & pipe in 3.20298910141

    5k trozos y una pequeña red de desarrollo inactivo (localhost): Done removing using scan in 1.26654982567 Done removing using scan_iter in 13.5976779461 Done removing using scan_iter & pipe in 4.66061878204 Done removing using scan & pipe in 1.13942599297

    cache.delete(*keys) de Dirk funciona bien, pero asegúrese de que las claves no redis.exceptions.ResponseError: wrong number of arguments for 'del' command vacías para evitar un redis.exceptions.ResponseError: wrong number of arguments for 'del' command .

    Si está seguro de que siempre obtendrá un resultado: cache.delete(*cache.keys('prefix:*') )

    Según mi prueba, costará demasiado tiempo si scan_iter solución scan_iter (como escribió Alex Toderita ).

    Por lo tanto, prefiero usar:

     from redis.connection import ResponseError try: redis_obj.eval('''return redis.call('del', unpack(redis.call('keys', ARGV[1])))''', 0, 'prefix:*') except ResponseError: pass 

    El prefix:* es el patrón.


    se refiere a: https://stackoverflow.com/a/16974060

    Por cierto, para el django-redis puede usar lo siguiente (de https://niwinz.github.io/django-redis/latest/ ):

     from django.core.cache import cache cache.delete_pattern("foo_*") 

    Puede usar un patrón específico para hacer coincidir todas las teclas y eliminarlas:

     import redis client = redis.Redis(host='192.168.1.106', port=6379, password='pass', decode_responses=True) for key in client.keys('prefix:*'): client.delete(key) 

    Utilice delete_pattern: https://niwinz.github.io/django-redis/latest/

     from django.core.cache import cache cache.delete_pattern("prefix:*")