Redis
 sql >> Datenbank >  >> NoSQL >> Redis

Das Redis-Wildcard-Löschskript mit EVAL, SCAN und DEL gibt Schreibbefehle zurück, die nach nicht deterministischen Befehlen nicht zulässig sind

AKTUALISIERUNG: Folgendes gilt für Redis-Versionen bis 3.2. Ab dieser Version hebt die effektbasierte Replikation das Verbot des Nicht-Determinismus auf, sodass alle Wetten ausgeschaltet (oder vielmehr eingeschaltet) sind.

Sie können (und sollten) den SCAN nicht mischen Befehlsfamilie mit jedem Schreibbefehl in einem Skript, da die Antwort des ersteren von den internen Redis-Datenstrukturen abhängt, die wiederum für den Serverprozess eindeutig sind. Anders ausgedrückt, es ist nicht garantiert, dass zwei Redis-Prozesse (z. B. Master und Slave) dieselben Antworten zurückgeben (im Redis-Replikationskontext [der nicht auf Operationen, sondern auf Anweisungen basiert], würde dies brechen).

Redis versucht sich gegen solche Fälle zu schützen, indem es jeden Schreibbefehl (wie DEL) blockiert ), wenn es nach einem zufälligen Befehl ausgeführt wird (z. B. SCAN sondern auch TIME , SRANDMEMBER und ähnliches). Ich bin mir sicher, dass es Möglichkeiten gibt, das zu umgehen, aber würdest du das tun wollen? Denken Sie daran, dass Sie sich auf unbekanntes Terrain begeben, wo das Verhalten des Systems nicht definiert ist.

Akzeptieren Sie stattdessen die Tatsache, dass Sie zufällige Lese- und Schreibvorgänge nicht mischen sollten, und versuchen Sie, sich einen anderen Ansatz zur Lösung Ihres Problems auszudenken, nämlich das Löschen einer Reihe von Schlüsseln nach einem Muster auf atomare Weise.

Fragen Sie sich zuerst, ob Sie eine der Anforderungen lockern können. Muss es atomar sein? Atomarität bedeutet, dass Redis für die Dauer des Löschvorgangs blockiert wird (unabhängig von der endgültigen Implementierung) und dass die Dauer des Vorgangs von der Größe des Auftrags abhängt (d. h. Anzahl der gelöschten Schlüssel und deren Inhalt [das Löschen eines großen Satzes ist teurer als zum Beispiel einen kurzen String zu löschen]).

Wenn Atomarität kein Muss ist, SCAN regelmäßig/faul und in kleinen Chargen löschen. Wenn es ein Muss ist, verstehen Sie, dass Sie im Grunde versuchen, die bösen KEYS zu emulieren Befehl :) Aber Sie können es besser machen, wenn Sie Vorkenntnisse über das Muster haben.

Unter der Annahme, dass das Muster während der Laufzeit Ihrer Anwendung bekannt ist, können Sie die relevanten Schlüssel (z. B. in einem Set) sammeln und diese Sammlung dann verwenden, um das Löschen auf atomare und replikationssichere Weise zu verwirklichen, was effizienter ist als das Durchlaufen des gesamten Schlüsselraums .

Das "schwierigste" Problem ist jedoch, wenn Sie einen Ad-hoc-Musterabgleich ausführen und gleichzeitig die Atomarität sicherstellen müssen. Wenn dies der Fall ist, läuft das Problem darauf hinaus, einen nach Mustern gefilterten Schnappschuss des Schlüsselraums zu erhalten, unmittelbar gefolgt von einer Reihe von Löschvorgängen (um es noch einmal zu betonen:während die Datenbank blockiert ist). In diesem Fall können Sie sehr gut KEYS verwenden innerhalb Ihres Lua-Skripts und hoffen Sie das Beste ... (aber wohl wissend, dass Sie auf SHUTDOWN NOSAVE zurückgreifen können ganz schnell :P).

Die letzte Optimierung besteht darin, den Schlüsselraum selbst zu indizieren. Beide SCAN und KEYS sind im Grunde vollständige Tabellenscans. Was wäre also, wenn wir diese Tabelle indizieren würden? Stellen Sie sich vor, Sie führen einen Index für Schlüsselnamen, die während einer Transaktion abgefragt werden können - Sie können wahrscheinlich eine sortierte Menge und lexikografische Bereiche verwenden (HT @TwBert ), um die meisten Musterabgleichsanforderungen zu beseitigen. Aber zu erheblichen Kosten ... Sie führen nicht nur eine doppelte Buchhaltung (Speichern der Namenskosten jedes Schlüssels in RAM und CPU), Sie wären auch gezwungen, Ihre Anwendung komplexer zu machen. Warum Komplexität hinzufügen? Denn um einen solchen Index zu implementieren, müssten Sie ihn selbst in der Anwendungsschicht (und möglicherweise alle Ihre anderen Lua-Skripte) pflegen und jeden Schreibvorgang sorgfältig in Redis in eine Transaktion einpacken, die auch den Index aktualisiert.

Angenommen, Sie haben all dies getan (und unter Berücksichtigung der offensichtlichen Fallstricke wie das Potenzial der zusätzlichen Komplexität für Fehler, mindestens doppelte Schreiblast auf Redis, RAM und CPU, Einschränkungen bei der Skalierung usw.), können Sie sich selbst auf die Sprünge helfen Schulter und beglückwünschen Sie sich selbst dafür, dass Sie Redis auf eine Weise verwenden, für die es nicht entwickelt wurde. Während kommende Versionen von Redis möglicherweise (oder auch nicht) bessere Lösungen für diese Herausforderung enthalten (@TwBert - möchten Sie ein gemeinsames RCP/Contrib machen und Redis erneut ein wenig hacken? ), bevor Sie dies versuchen, bitte ich Sie dringend, die ursprünglichen Anforderungen zu überdenken und zu überprüfen, ob Sie Redis korrekt verwenden (d. h. Ihr "Schema" gemäß Ihren Datenzugriffsanforderungen entwerfen).