Redis: Unerwartete Räumungen mit LRU-Richtlinien

Erstellt am 29. Juni 2015  ·  26Kommentare  ·  Quelle: redis/redis

Das verhält sich also wirklich seltsam und wir konnten nach einigen Stunden des Testens nicht verstehen, warum. Wir haben diesen Master Redis 3.0.2 mit einem angeschlossenen Slave und verwenden 3 Datenbanken.

Das Problem ist, dass mit volatile-ttl , allkeys-lru oder volatile-lru Schlüssel mit einer Ablaufzeit von 2 Jahren (PHP-Sitzungen) entfernt werden, bevor Schlüssel mit 2 Stunden.

Mein Verständnis wäre, dass diese langen TTL-Schlüssel zuletzt entfernt werden sollten, oder mir fehlt etwas? Außerdem wird ein neu hinzugefügter Schlüssel immer "weniger verwendet" als ein alter, der einige Treffer hatte, aber in meinem Fall möchte ich diese Schlüssel nicht entfernen. Wird SET nicht als "Nutzung" betrachtet?

Auch spielt die Reihenfolge der DBs für die Räumung eine Rolle? Ich hatte das Gefühl, dass es überwiegend aus DB 0 entfernt wird, aber ich habe sie in die letzte DB verschoben, und diese Objekte werden immer noch sofort nach dem Schreiben entfernt, wenn Speicherdruck besteht.

non critical bug state-work-needed

Hilfreichster Kommentar

UPDATE: Ich habe eine funktionierende Lösung dafür, ich verbessere sie und überprüfe Geschwindigkeitsregressionen. Wird eine große Menge an Code geändert, so ist nicht sicher, ob es 3.2 ergibt oder in unstable verbleibt.

Alle 26 Kommentare

Hallo, wird dabei nur ein _given_ Schlüssel entfernt, während andere bessere Schlüssel vorhanden waren, oder etwas mit statistischer Signifikanz? Da der Redis LRU-Algorithmus eine Annäherung ist, kann Folgendes passieren, wenn Sie viele Schlüssel mit einer sehr langen TTL (nennen wir sie Set "A") und Schlüssel mit kurzer TTL (Set "B") haben:

  1. Sie haben 1000 Schlüssel in Satz A.
  2. Sie haben 10 Schlüssel in Satz B.

Die Redis LRU-Näherung tastet die DB für 5 zufällige Schlüssel ab, erhält jedoch nur Schlüssel aus Satz A, da sie so stark mit Satz B verglichen werden. Daher wird die beste aus der erhaltenen Stichprobe ausgewählt und ein Schlüssel aus A anstelle eines aus B abgelaufen. Ist das vielleicht der Fall? Wenn ja, ist dies ein erwartetes Verhalten.

Dies kann auch passieren, wenn Sie die gleiche Anzahl von Schlüsseln in Satz A und B haben, dies ist jedoch statistisch gesehen selten und sollte für einen Cache nicht schlecht sein, da Redis in den meisten Fällen A-Schlüssel abläuft.

DBs sind für die Räumung nicht allzu wichtig, aber ein bisschen wichtig ... Schlüssel werden nacheinander aus jeder DB entfernt, sodass die oben erläuterte Abtastung in jeder DB auf lokale Weise durchgeführt wird, sodass a abläuft wenige Schlüssel von jeder DB.

Wenn Sie Schlüssel A in DB 0 und Schlüssel B in DB 1 haben, sehen Sie aus diesem Grund, dass Schlüssel aus Satz A mit der gleichen Rate abgelaufen sind wie Schlüssel aus Satz B.

ps Es sollte möglich sein, den Algorithmus zu ändern, um eine DBU-übergreifende Kohärenz der LRU zu erreichen. Mal sehen, was das Problem ist, bevor wir fortfahren ...

Vielen Dank für die schnelle Antwort.

Die Größe der DBs ist in der Tat sehr unausgewogen:

# Keyspace
db0:keys=8,expires=6,avg_ttl=2591868775
db1:keys=80,expires=70,avg_ttl=3313829
db2:keys=183102,expires=183100,avg_ttl=5698824

In meinem Fall leben die Sitzungen in DB0 mit einer Ablaufzeit von 1 Jahr, einem Anwendungscache in DB1 mit einem Ablauf von ungefähr 1 Tag (Routen, Ansichten usw.) und in DB2 sind die Dinge, die ich gerne entfernt hätte - die ältesten zuerst , mit einem Ablauf von 2 Stunden, mit Massendaten, die von einem Cron-Job ausgefüllt werden.

Bestätigt dies Ihre Theorie?

Genau bestätigt ... Es ist nicht optimal, wie es tatsächlich funktioniert, aber ich bin nicht sicher, ob wir es beheben können, ohne die Leistung stark zu beeinträchtigen. Wenn Sie alles zu einer einzelnen Datenbank hinzufügen, wird dies übrigens behoben. Sind Sie jedoch in der Stimmung, in den nächsten Tagen ein paar Patches auszuprobieren? Vielen Dank.

Das Hinzufügen zu derselben Datenbank wäre nicht gut, da wir von Zeit zu Zeit DB1 oder DB2 löschen, aber nicht die Sitzungen. Ich würde davon ausgehen, dass auch die Verwendung verschiedener Instanzen das Problem lösen würde. Ich habe irgendwo gelesen, dass Sie die Funktion für mehrere DBs sowieso ablehnen möchten. Sollten wir stattdessen einen Cluster verwenden? Ich bin mir nicht sicher, was das entsprechende Setup wäre.

Klar, wir können einige Patches ausprobieren, ich habe es ziemlich leicht in unserer Umgebung reproduzierbar.

Es wird in naher Zukunft sicher nicht veraltet sein, aber es ist keine Funktion von Redis Cluster. Daher wird empfohlen, mehrere DBs mit Bedacht einzusetzen. Das Entfernen aller Schlüssel in einer einzelnen Datenbank ist sinnvoll, wenn Sie keine latenzempfindlichen Workloads haben. Andernfalls ist es meiner Meinung nach besser, eine einzelne Datenbank zu verwenden und einfach verschiedene Präfixe für Schlüssel verschiedener Gruppen zu verwenden und bei Bedarf mithilfe von SCAN-Mustern + DEL-Operationen (variadic) massenweise zu entfernen. Auf diese Weise können Sie massenweise entfernen, ohne den Server mit FLUSHDB zu blockieren. Gleichzeitig lösen Sie Ihr Räumungsproblem.

Ich denke immer noch, dass es sich lohnt, mit ein paar Patches zu experimentieren, aber ich denke, dass an diesem Punkt, an dem ich Ihren Anwendungsfall kenne, Ihr Hauptproblem darin besteht, dass FLUSHDB blockiert (es wird in Zukunft nicht mehr blockieren). Übrigens gibt es ein Problem).

Das Blockieren mit FLUSHDB ist kein so großes Problem, da es sehr rückwärts passiert, aber das Räumungsproblem wirkt sich sehr schlecht auf die Site aus, da Sie sich nicht anmelden können, da die Sitzung gespeichert und sofort beendet wird.

Vielleicht wäre ein neuer Algorithmus eine gute Idee? Da kommt es auf den Anwendungsfall an ..

Ja, ich werde es mit dem neuen Algo versuchen. Mal sehen, ob es so schnell wie das aktuelle gemacht werden kann ... News ASAP.

In der Zwischenzeit, wie mein Kollege zu diesem Problem feststellte, wird

Wenn ich diese Diskussion richtig verstehe, erfolgt die LRU-Bereinigung pro Datenbank? Wir sehen Probleme, wenn wir eine Redis-Instanz verwenden, die als LRU-Cache für Rails konfiguriert ist, und wir verwenden separate DBs pro Stufe (Produktion, Staging usw.). In die Staging-DBs geschriebene Schlüssel werden fast augenblicklich entfernt - ich vermute, weil die Produktions-DB fast den gesamten Speicher belegt. Gibt es eine Bewegung, um die Räumung in allen DBs zu ermöglichen, oder müssen wir die App neu strukturieren, um Schlüssel mit Namespace in dieselbe DB zu schreiben?

@cheald Auf lange Sicht gibt es meine Absicht, dies zu beheben, es kann jedoch einige nicht triviale Änderungen erforderlich sein, um zu vermeiden, dass die Leistung des LRU-Algorithmus vollständig beeinträchtigt wird eine große Veränderung (anscheinend), damit es mit den aktuellen Redis gut funktioniert. Ich bin mir momentan nicht sicher, wann / wie dies behoben wird, aber technisch gesehen wird der Fehlerbericht akzeptiert und es besteht die Bereitschaft, ihn zu beheben.

UPDATE: Ich habe eine funktionierende Lösung dafür, ich verbessere sie und überprüfe Geschwindigkeitsregressionen. Wird eine große Menge an Code geändert, so ist nicht sicher, ob es 3.2 ergibt oder in unstable verbleibt.

Schön, wie wird der Algorithmus jetzt funktionieren? oder ist es ein völlig anderer (neuer) Algorithmus?

Wir haben uns gerade darauf vorbereitet, unsere Redis-Instanzen zu teilen, aber wenn es in 3.2 landet, könnten wir es versuchen.

Haben Sie darüber nachgedacht, zusätzlich zur Aktualität eine Zulassungsrichtlinie zu verwenden , um die Häufigkeit zu berücksichtigen? Bleibt O (1) und bietet gleichzeitig nahezu optimale Trefferquoten.

@ ben-manes hey, ich arbeite gerade an der LFU. Eine erste Implementierung finden Sie im Zweig lfu auf Github. Ich hatte 24 Bit Platz im Objekt, um dies zu implementieren, aber AFAIK funktioniert ziemlich gut und ich erhalte bessere Ergebnisse mit Zugriffsmustern nach dem Potenzgesetz im Vergleich zum ursprünglichen LRU-Algo von Redis.

@razvanphp Die Lösung für dieses Problem ist eine nicht triviale Änderung des ursprünglichen LRU-Algorithmus. Ich habe das Gefühl, dass ich nicht wieder in 3.2 zurückkehren werde, sondern stattdessen versuchen werde, so schnell wie möglich auf 4.0 zu drängen.

LFU ist optimal für das Potenzgesetz, aber für viele reale Workloads weniger intelligent. Mein Vorschlag kann sehr kompakt sein und erzielt nahezu optimale Trefferquoten über eine Vielzahl von Spuren. Bitte werfen Sie einen Blick auf den obigen Link und das Forschungspapier.

@ Ben-Manes liest gerade die Zeitung. Beachten Sie, dass das, was ich implementiert habe, das Altern unterstützt, sodass es keine naive LFU ist und sich im Laufe der Zeit anpasst. Es sollte sich also nicht von dem unterscheiden, was in diesem Artikel beschrieben wird, wenn ich die Zusammenfassung richtig verstehe. Lesen Sie jedoch alles, um die Details zu überprüfen. Vielen Dank.

Ein weiterer zu berücksichtigender Aspekt ist, dass das Design des in dem Papier vorgeschlagenen Caches für Redis aufgrund des Konzepts der Zulassungsrichtlinie möglicherweise nicht akzeptabel ist. Wir möchten zwar, dass Redis die Dinge auf intelligente Weise räumt, bin mir aber nicht sicher, ob dies der Fall ist Es ist möglich, Schreibvorgänge vollständig zu ignorieren.

Die meisten Schreibvorgänge sind One-Hit-Wunder und Umweltverschmutzung, was eine Ursache für die Verschlechterung der LRU ist. Durch den größeren Verlauf und das optionale Fenster wird vermieden, dass gute Schreibvorgänge ignoriert werden.

Übrigens liegt der Unterschied zwischen LFU und TinyLFU (LFU mit längerer Geschichte) in meinen Spuren zwischen 0 und 20%. Der Unterschied ist bei Such- und Analyse-Workloads groß und bei Datenbanken kleiner (5% +). In diesen Spuren ist LFU erwartungsgemäß immer noch viel besser als LRU, egal was Sie in einem großen Cache gewinnen werden. Wenn Sie Redis-Spuren erfassen können, können wir im Idealfall die verschiedenen Optionen simulieren, um ein fundiertes Design zu erstellen.

@ ben-manes Ich habe versucht, eine datengesteuerte Entwicklung mit den Tests durchzuführen, die Sie im Verzeichnis utils/lru in der instabilen Redis-Distribution finden, und mit dem Power-Law-Zugriffs-Treffer- / Fehler-Messgerät redis-cli und AFAIK diese "fenstergesteuerte" LFU funktioniert im Vergleich zur früheren LRU sehr gut. Ich bin mir jedoch nicht sicher, wie ich es mit anderen Tests besser messen kann. Ich denke, alles wird ziemlich synthetisch, da es das große Problem des Zugriffszeitpunkts gibt, das sich unter den Workloads erheblich ändern kann. Daher erwarte ich die neuen Benutzer von Redis 4.0 (dort wird dieser Code veröffentlicht), um zu testen und zu melden, ob LFU gut funktioniert und wie es verbessert werden kann.

Übrigens hat der LFU-Algorithmus zwei einstellbare Parameter, den logarithmischen Faktor und die Abklingzeit, so dass eingestellt werden kann, wie aggressiv der LFU-Zähler mit den Zugriffen zunimmt und wie oft er im Laufe der Zeit durch zwei geteilt wird. Dies sollte helfen, mit verschiedenen Abstimmungsfaktoren zu experimentieren.

Jede Änderung, die die aktuelle Trefferquote verbessern kann, ohne die Leistung zu beeinträchtigen, ist sehr willkommen. Es ist eine interessante Idee, sich an den Verlauf von Schlüsseln zu erinnern, die sich derzeit nicht im Datensatz befinden, wie er von dem von Ihnen verlinkten Papier ausgenutzt wurde, und meine LFU-Implementierung tut dies zumindest ein wenig: Wenn ein Schlüssel durch einen anderen Wert ersetzt wird, aber Mit dem gleichen Namen achten wir darauf, die LFU-Geschichte der Hits nicht zu vergessen.

Die allgemeine Anwendung dieses Konzepts auf Redis ist jedoch ein Problem, da das von Ihnen erwähnte Papier auf reine GET / SET-Caches ausgerichtet ist. Ein Redis-Benutzer erwartet kaum, dass ein Befehl, selbst wenn maxmemory gesetzt ist, möglicherweise überhaupt keine Auswirkung hat, da der Caching-Algorithmus dachte, dass auf diesen Schlüssel nicht zugegriffen werden kann, was eine große Verhaltensänderung bedeuten würde.

Stattdessen werden jetzt immer Schlüssel erstellt und für einige Zeit genommen, bevor sie entfernt werden. Dies ist viel weniger überraschend, auch wenn es etwas weniger effektiv ist, wenn Redis nur als Cache verwendet wird.

Allgemeines Update für dieses Problem: Die DB-übergreifenden Probleme von Redis laufen jetzt in der Verzweigung unstable , die vor Ende Oktober Redis 4.0 sein wird. Also schließe ich dieses Problem. Vielen Dank für Ihre Hilfe.

Vielen Dank für die nachdenkliche Antwort @antirez.

Viele Räumungsrichtlinien sind jetzt segmentiert, um Einträge mit geringem Wert aggressiver zu entfernen. SLRU hat eine "Bewährung", S4LRU hat Ebenen, LIRS hat eine "HIR" -Region, ARC hat T1, 2Q hat eine IN-Warteschlange usw. Diese Richtlinien basieren auf Aktualität und werden bei einem nachfolgenden Zugriff aus der Region mit niedrigem Wert gefördert. Die fortgeschritteneren verwenden "Geistereinträge" (entfernte Schlüssel), um einen längeren Verlauf zu überwachen und zu versuchen, zu korrigieren, wenn sie einen Eintrag vorzeitig entfernt haben. Auf diese Weise können auf Aktualität basierende Richtlinien Frequenzinformationen erfassen.

TinyLFU ist frequenzbasiert und kann als einfacher Filter angesehen werden. Der "Zulassungsfenster" -Ansatz (W-TinyLFU) vermeidet das sofortige Filtern und ähnelt damit den oben genannten Richtlinien. In diesem Modus ermöglicht es einen neuen Eintrag in den Cache und verzögert die Filterung, bis keine hohe Aktualität mehr vorliegt. Die Regionen können jede Räumungsrichtlinie, z. B. S / LRU in der Zeitung, als die optimalste verwenden (aber zufällig ist sehr gut). Mit dieser Variante würden Sie die Benutzererwartungen nicht brechen, indem Sie für SET-Vorgänge "überhaupt keine Auswirkung" haben.

Ich fand synthetische Tests (z. B. Zipf) weniger interessant als reale Spuren. Ich habe bereits veröffentlichte verwendet, aber es wäre einfach, andere Workloads zu simulieren, wenn Sie einige von Redis-Benutzern erwerben könnten. Der Simulator enthält viele Richtlinien und Varianten, einschließlich der kürzlich erfolgten Einführung von TuQ durch

@antirez

Bitte beachten Sie die Spuren in Koffein Nr. 106, die eine Arbeitsbelastung zeigen, bei der LRU optimal ist. Dies führt dazu, dass Richtlinien, die auf Aktualität basieren, aber versuchen, die Häufigkeit abzuleiten, mit der LRU übereinstimmen. Richtlinien, die auf Häufigkeit basieren und versuchen, auf Aktualität zu schließen, zeigen jedoch eine Underperformance. Das Histogramm mit längerer Frequenz in TinyLFU führt dazu, dass es länger leidet als LFU. Die Variante mit Zulassungsfenster hilft, den Aufprall zu reduzieren, bleibt aber leicht benachteiligt.

Diese Arbeitslast kann Ihnen helfen, die LFU von Redis zu analysieren, um festzustellen, wie gut sie sich anpasst.

Warum nicht das WHITE LIST- oder / und BLACK LIST-Muster implementieren, um die Räumung von Schlüsseln zu verhindern oder zuzulassen, die mit session_* , oder um den Datenbankzugriff darauf zu beschränken, falls erforderlich?

War diese Seite hilfreich?
0 / 5 - 0 Bewertungen