webentwicklung-frage-antwort-db.com.de

Redis Python - So löschen Sie alle Schlüssel nach einem bestimmten Muster In Python, ohne dass Python durchläuft

Ich schreibe einen Django-Verwaltungsbefehl, um einige unserer Redis-Zwischenspeicherungen auszuführen. Grundsätzlich muss ich alle Schlüssel auswählen, die ein bestimmtes Muster bestätigen (zum Beispiel: "Präfix: *") und sie löschen.

Ich weiß, dass ich das cli dazu benutzen kann:

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

Aber ich muss das aus der App heraus tun. Also muss ich die Python-Bindung verwenden (ich verwende py-redis). Ich habe versucht, eine Liste in delete einzufügen, die jedoch fehlschlägt:

from common.redis_client import get_redis_client
cache = get_redis_client()
x = cache.keys('prefix:*') 

x == ['prefix:key1','prefix:key2'] # True

# Und nun

cache.delete(x) 

# gibt 0 zurück. nichts wird gelöscht

Ich weiß, dass ich über x iterieren kann:

for key in x:
   cache.delete(key)

Aber das würde Redis unglaubliche Geschwindigkeit verlieren und seine Fähigkeiten missbrauchen. Gibt es eine Pythonic-Lösung mit Py-Redis, ohne Iteration und/oder Cli?

Vielen Dank!

22
alonisser

Ich denke der 

 for key in x: cache.delete(key)

ist ziemlich gut und prägnant. delete möchte wirklich nur einen Schlüssel auf einmal haben, also müssen Sie eine Schleife machen.

Andernfalls weist diese vorherige Frage und Antwort auf eine Lua-basierte Lösung hin.

8

Verwenden Sie SCAN-Iteratoren: https://pypi.python.org/pypi/redis

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

Aus der Dokumentation

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

Es wird lediglich ein Argument pro Schlüssel gelöscht, und Sie erfahren, wie viele davon gefunden und gelöscht wurden.

Ich glaube, dass Sie im Fall Ihres oben genannten Codes Folgendes tun können:

    redis.delete(*x)

Aber ich gebe zu, ich bin neu in Python und mache es einfach:

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

Die cache.delete(*keys)-Lösung von Dirk funktioniert gut, aber stellen Sie sicher, dass die Schlüssel nicht leer sind, um ein redis.exceptions.ResponseError: wrong number of arguments for 'del' command zu vermeiden. 

Wenn Sie sicher sind, dass Sie immer ein Ergebnis erhalten: cache.delete(*cache.keys('prefix:*') )

4
Blackeagle52

Hier ist ein vollständiges Funktionsbeispiel mit py-redis :

from redis import StrictRedis
cache = StrictRedis()

def clear_ns(ns):
    """
    Clears a namespace
    :param ns: str, namespace i.e 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

Sie können auch scan_iter ausführen, um alle Schlüssel in den Arbeitsspeicher zu übernehmen, und dann alle Schlüssel an delete übergeben, um eine Massenlöschung durchzuführen, aber für große Namespaces ist möglicherweise ein guter Speicherplatz erforderlich. Also am besten eine delete für jeden Schlüssel ausführen.

Prost! 

AKTUALISIEREN:

Seit dem Schreiben der Antwort begann ich mit der Pipelining-Funktion von redis, um alle Befehle in einer Anfrage zu senden und Netzwerklatenz zu vermeiden:

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 i.e 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

UPDATE2 (beste Leistung): 

Wenn Sie scan anstelle von scan_iter verwenden, können Sie die Blockgröße steuern und den Cursor mit Hilfe Ihrer eigenen Logik durchlaufen. Dies scheint auch viel schneller zu sein, insbesondere wenn Sie mit vielen Tasten arbeiten. Wenn Sie das Pipelining hinzufügen, erhalten Sie ein bisschen mehr Leistung, je nach Chunk-Größe 10-25%, zu Lasten der Speicherauslastung, da Sie den Befehl zum Ausführen nicht an Redis senden, bis alles generiert ist. Also blieb ich beim scannen:

from redis import StrictRedis
cache = StrictRedis()
CHUNK_SIZE = 5000

def clear_ns(ns):
    """
    Clears a namespace
    :param ns: str, namespace i.e 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

Hier sind einige Benchmarks:

5-KB-Blöcke mit einem belegten Redis-Cluster: 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-Brocken und ein kleines Leerlauf-Gerät (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

4
radtek

Laut meinem Test kostet es zu viel Zeit, wenn ich scan_iter-Lösung verwende (wie Alex Toderita schrieb ).

Daher bevorzuge ich:

from redis.connection import ResponseError

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

Der prefix:* ist das Muster.


verweist auf: https://stackoverflow.com/a/16974060

2
carton.swing

Sie können ein bestimmtes Muster verwenden, um alle Schlüssel abzugleichen und diese zu löschen:

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)
1
Lynn Han

Übrigens, für die Django-Redis können Sie Folgendes verwenden (von https://niwinz.github.io/Django-redis/latest/ ):

from Django.core.cache import cache
cache.delete_pattern("foo_*")
1
Gleb

Verwenden Sie delete_pattern: https://niwinz.github.io/Django-redis/latest/

from Django.core.cache import cache
cache.delete_pattern("prefix:*")
0
Jijo