Holen Sie sich alle Schlüssel in der Redis-Datenbank mit Python

76

Es gibt einen Beitrag über einen Redis-Befehl, um alle verfügbaren Schlüssel abzurufen, aber ich würde es gerne mit Python tun.

Wie kann man das machen?

tscizzle
quelle

Antworten:

112

Verwenden scan_iter()

scan_iter()ist keys()für eine große Anzahl von Schlüsseln überlegen, da Sie einen Iterator erhalten, den Sie verwenden können, anstatt zu versuchen, alle Schlüssel in den Speicher zu laden.

Ich hatte 1B-Datensätze in meinem Redis und konnte nie genug Speicher bekommen, um alle Schlüssel auf einmal zurückzugeben.

SCANNING KEYS ONE-BY-ONE

Hier ist ein Python-Snippet, mit scan_iter()dem alle Schlüssel, die einem Muster entsprechen, aus dem Store abgerufen und einzeln gelöscht werden:

import redis
r = redis.StrictRedis(host='localhost', port=6379, db=0)
for key in r.scan_iter("user:*"):
    # delete the key
    r.delete(key)

SCANNEN IN BATCHES

Wenn Sie eine sehr große Liste von Schlüsseln zum Scannen haben, z. B. mehr als 100.000 Schlüssel, ist es effizienter, sie in Stapeln wie folgt zu scannen:

import redis
from itertools import izip_longest

r = redis.StrictRedis(host='localhost', port=6379, db=0)

# iterate a list in batches of size n
def batcher(iterable, n):
    args = [iter(iterable)] * n
    return izip_longest(*args)

# in batches of 500 delete keys matching user:*
for keybatch in batcher(r.scan_iter('user:*'),500):
    r.delete(*keybatch)

Ich habe dieses Skript verglichen und festgestellt, dass die Verwendung einer Stapelgröße von 500 fünfmal schneller war als das Scannen von Schlüsseln nacheinander. Ich habe verschiedene Chargengrößen (3,50,500,1000,5000) getestet und festgestellt, dass eine Chargengröße von 500 optimal zu sein scheint.

Beachten Sie, ob Sie das scan_iter()oder verwendenkeys() , dass die Operation Methode verwenden, nicht atomar ist und teilweise fehlschlagen kann.

Vermeiden Sie definitiv die Verwendung von XARGS in der Befehlszeile

Ich empfehle dieses Beispiel nicht, das ich an anderer Stelle wiederholt gefunden habe. Es schlägt bei Unicode-Schlüsseln fehl und ist selbst bei einer moderaten Anzahl von Schlüsseln unglaublich langsam:

redis-cli --raw keys "user:*"| xargs redis-cli del

In diesem Beispiel erstellt xargs für jeden Schlüssel einen neuen Redis-Cli-Prozess! das ist schlecht.

Ich habe diesen Ansatz als 4-mal langsamer als das erste Python-Beispiel bewertet, bei dem jeder Schlüssel einzeln und 20-mal langsamer gelöscht wurde als beim Löschen in Stapeln von 500.

Patrick Collins
quelle
Ich erhalte immer wieder "redis.exceptions.ResponseError: unbekannter Befehl 'SCAN'", wenn ich über r.scan_iter () iteriere. Irgendeine Idee warum? Ich habe noch keine Antwort gefunden.
BringBackCommodore64
1
@ BringBackCommodore64 Ihre Version von redis ist zu alt, installieren Sie eine neue.
Piokuc
@piokuc Nun, ich habe meine Redis nicht aktualisiert, aber deine Vermutung scheint offensichtlich richtig zu sein!
BringBackCommodore64
@ BringBackCommodore64 Es ist keine Vermutung. Ich hatte das gleiche Problem, Upgrade hat das gelöst. Ich kann mich nicht an die Version erinnern, die ich hatte und die den SCAN nicht unterstützte, aber sie war einige Jahre alt. Jede neuere Version von Redis sollte in Ordnung sein.
Piokuc
1
@LeiYang Redis Suche erlaubt Globs / Wildcards. Also "mykey *", "user_ ", "user: ". redis.io/commands/keys
Patrick Collins
57

Ja, verwenden Sie keys()aus dem StrictRedis-Modul:

>>> import redis
>>> r = redis.StrictRedis(host=YOUR_HOST, port=YOUR_PORT, db=YOUR_DB)
>>> r.keys()

Wenn Sie ein Nullmuster angeben, werden alle abgerufen. Gemäß der verlinkten Seite:

Tasten (Muster = '*')

Gibt eine Liste der Schlüssel zurück, die dem Muster entsprechen

fedorqui 'SO hör auf zu schaden'
quelle
14
Beachten Sie, dass von der Verwendung dieses Befehls auf Produktionsservern abgeraten wird. Wenn Sie über eine hohe Anzahl von Schlüsseln verfügen, antwortet Ihre Redis-Instanz während der Verarbeitung dieser Anfrage nicht auf andere Anforderungen. Dies kann ziemlich lange dauern.
Pascal Le Merrer
2
Erwägen Sie das Hinzufügen eines Verweises auf den SCANBefehl, da dies jetzt eine bevorzugte Methode ist, um alle Schlüssel mit der O (1) -Zeitkomplexität jeder Anforderung abzurufen. (und O (N) für alle Anfragen)
Kirill Zaitsev
2
r.keys()ist ziemlich langsam, wenn Sie versuchen, ein Muster abzugleichen und nicht nur alle Schlüssel zurückzugeben. scan
Erwägen
2
@KonstantineNikolaou Ich habe das OP benachrichtigt und er hat meine Antwort gerne nicht akzeptiert, um die andere zu akzeptieren. Vielen Dank für die Berichterstattung, ich hatte dies vor so langer Zeit verwendet, aber jetzt fehlt mir der Fokus auf das Thema, um zu überprüfen, was am besten ist.
Fedorqui 'SO hör auf zu schaden'
@ Fedorqui froh zu hören, dass 😊
cnicolaou
11
import redis
r = redis.Redis("localhost", 6379)
for key in r.scan_iter():
       print key

mit Pyredis Bibliothek

Scan-Befehl

Verfügbar seit 2.8.0.

Zeitliche Komplexität: O (1) für jeden Anruf. O (N) für eine vollständige Iteration, einschließlich genügend Befehlsaufrufe, damit der Cursor auf 0 zurückkehrt. N ist die Anzahl der Elemente in der Sammlung.

Schwarzer Reiter
quelle
0

Ich möchte einen Beispielcode hinzufügen, der zu Patricks Antwort und anderen passt.
Dies zeigt die Ergebnisse sowohl mit Schlüsseln als auch mit der scan_iter-Technik. Beachten Sie außerdem, dass Python3 zip_longest anstelle von izip_longest verwendet. Der folgende Code durchläuft alle Schlüssel und zeigt sie an. Ich habe die Stapelgröße als Variable auf 12 gesetzt, um die Ausgabe zu verkleinern.

Ich habe dies geschrieben, um besser zu verstehen, wie das Stapeln von Schlüsseln funktioniert.

import redis
from itertools import zip_longest

\# connection/building of my redisObj omitted here

\# iterate a list in batches of size n
def batcher(iterable, n):
    args = [iter(iterable)] * n
    return zip_longest(*args)
    
result1 = redisObj.get("TestEN")
print(result1)
result2 = redisObj.get("TestES")
print(result2)

print("\n\nLoop through all keys:")
keys = redisObj.keys('*')
counter = 0
print("len(keys)=", len(keys))
for key in keys:
    counter +=1
    print (counter, "key=" +key, " value=" + redisObj.get(key))

print("\n\nLoop through all keys in batches (using itertools)")
\# in batches of 500 delete keys matching user:*
counter = 0
batch_counter = 0
print("Try scan_iter:")
for keybatch in batcher(redisObj.scan_iter('*'), 12):
    batch_counter +=1
    print(batch_counter, "keybatch=", keybatch)
    for key in keybatch:
        if key != None:
            counter += 1
            print("  ", counter, "key=" + key, " value=" + redisObj.get(key))

Beispielausgabe:

Loop through all keys:
len(keys)= 2
1 key=TestES  value=Ola Mundo
2 key=TestEN  value=Hello World


Loop through all keys in batches (using itertools)
Try scan_iter:
1 keybatch= ('TestES', 'TestEN', None, None, None, None, None, None, None, None, None, None)
   1 key=TestES  value=Ola Mundo
   2 key=TestEN  value=Hello World

Beachten Sie, dass Redis-Befehle Single-Threaded sind. Wenn Sie also eine Taste () ausführen, können Sie andere Redis-Aktivitäten blockieren. Sehen Sie hier einen ausgezeichneten Beitrag, der dies ausführlicher erklärt: SCAN vs KEYS-Leistung in Redis

NealWalters
quelle