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!
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.
Verwenden Sie SCAN-Iteratoren: https://pypi.python.org/pypi/redis
for key in r.scan_iter("prefix:*"):
r.delete(key)
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')
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:*') )
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
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
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)
Ü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_*")
Verwenden Sie delete_pattern: https://niwinz.github.io/Django-redis/latest/
from Django.core.cache import cache
cache.delete_pattern("prefix:*")