So können Sie eine Hierarchie effizient aus dem Cache speichern und zurücklesen

8

Meine Situation ist, dass ich momentan eine Hierarchie in einer SQL-Datenbank speichere, die sich schnell an 15000 Knoten (5000 Kanten) annähert. Diese Hierarchie definiert mein Sicherheitsmodell basierend auf einer Benutzerposition in der Baumstruktur und gewährt Zugriff auf die folgenden Elemente. Wenn also ein Benutzer eine Liste aller gesicherten Objekte anfordert, benutze ich CTE, um ihn in der db zu rekursivieren (und alle Items zu reduzieren), der gestartet wird, um sein Alter anzuzeigen (langsam).

Die Hierarchie ändert sich nicht oft, also habe ich versucht, sie in das RAM zu verschieben (redis). Ich habe im Hinterkopf viele Subsysteme, die dies für Sicherheitsaufrufe benötigen, und Benutzeroberflächen, um den Baum für CRUD-Operationen zu erstellen.

Erster Versuch

Mein erster Versuch besteht darin, die Beziehungen als Schlüsselwertpaar zu speichern (So ​​ist es in der Datenbank gespeichert)

%Vor%

Wenn ich also E und all seine Nachkommen will, rekrutiere ich rekursiv sein Kind und sein Kind mit den Schlüsseln, und es erlaubt mir, an jedem Knoten zu beginnen, nach unten zu gehen. Diese Lösung ergab eine gute Geschwindigkeitssteigerung, aber mit 15.000 Knoten waren es ungefähr 5000 Cachetreffer, um meinen Baum im Code neu aufzubauen (Worst Case Szenario ... beginnend bei E. basiert die Leistung auf dem Startknotenpunkt, was dazu führt, dass Superuser den schlechteste Leistung). Das war immer noch ziemlich schnell, schien aber gesprächig zu sein. Ich mag die Tatsache, dass ich einen Knoten jederzeit entfernen kann, indem ich ihn aus der Liste der Schlüssel heraushole, ohne meinen gesamten Cache neu aufzubauen. Dies wurde auch schnell beleuchtet, um einen Baum bei Bedarf visuell auf einer Benutzeroberfläche zu erstellen.

Zweiter Versuch

Meine andere Idee ist es, die Hierarchie von der Datenbank zu nehmen, den Baum zu bauen und diesen im RAM zu speichern (Redis) und dann das Ganze aus dem Speicher zu ziehen (es war ungefähr 2 MB groß, serialisiert). Dies gab mir einen einzigen Aufruf (nicht so gesprächig) in redis, um den gesamten Baum herauszuziehen, den übergeordneten Knoten des Benutzers zu finden und abzusteigen, um alle untergeordneten Elemente zu erhalten. Diese Anrufe sind häufig und 2 MB auf der Netzwerkschicht schien groß zu sein. Das bedeutet auch, dass ich nicht einfach hinzufügen / entfernen und Elemente hinzufügen kann, ohne den Baum herunterzureißen und zu bearbeiten und alles zurückzuschieben. Auch bei Bedarf erzeugten Bäume über HTTP, jede Anfrage musste 2MB herunterziehen, um nur direkte Kinder zu bekommen (sehr klein mit der ersten Lösung).

Welche Lösung ist Ihrer Meinung nach ein besserer Ansatz (langfristig, wenn er weiter wächst). Beide sind trotzig schneller und entlasten die Datenbank. Oder ist das ein besserer Weg, dies zu erreichen, an den ich nicht gedacht habe?

Danke

    
Waterboy4800 15.11.2011, 23:37
quelle

3 Antworten

3

Lass mich eine Idee anbieten ...

Verwenden Sie hierarchische Versionierung . Wenn ein Knoten im Diagramm geändert wird, inkrementiere seine Version (ein einfaches int-Feld in der Datenbank), aber auch inkrementiert Versionen aller seiner Vorfahren.

  • Wenn Sie zum ersten Mal eine Unterstruktur von der Datenbank abrufen, speichern Sie sie im RAM. (Sie können dies wahrscheinlich durch rekursives CTE optimieren und tun dies in einem einzigen Datenbank-Umlauf.)
  • Wenn Sie jedoch das nächste Mal den gleichen Unterbaum abrufen müssen, rufen Sie nur den Stamm ab. Vergleichen Sie dann die zwischengespeicherte Version mit der gerade von der Datenbank abgerufenen Version.
    • Wenn sie übereinstimmen, großartig, können Sie den Abruf stoppen und den Cache einfach wiederverwenden.
    • Wenn dies nicht der Fall ist, rufen Sie die untergeordneten Elemente ab und wiederholen Sie den Vorgang, indem Sie den Cache aktualisieren, während Sie fortfahren.

Das Endergebnis besteht darin, dass Sie das Holen sehr oft abbrechen, normalerweise nach nur einem Knoten, und Sie müssen nicht einmal den gesamten Graphen zwischenspeichern. Änderungen sind teuer, aber das sollte kein Problem sein, da sie selten sind.

Übrigens würde ein ähnliches Prinzip in umgekehrter Richtung funktionieren - d. h. wenn Sie mit einem Blatt beginnen und den Pfad zur Wurzel finden müssen. Sie müssten die Versionshierarchie in die entgegengesetzte Richtung aktualisieren, aber der Rest sollte in sehr ähnlicher Weise funktionieren. Sie könnten sogar beide Richtungen kombinieren.

--- BEARBEITEN ---

Wenn Ihre Datenbank und der ADO.NET-Treiber dies unterstützen, könnte es sich lohnen, Serverbenachrichtigungen zu prüfen, z. B. MS SQL Server SqlDependency oder OracleDependency .

Im Wesentlichen weisen Sie das DBMS an, Änderungen zu überwachen und Sie zu benachrichtigen, wenn sie auftreten. Dies ist ideal, um Ihren clientseitigen Cache effizient auf dem neuesten Stand zu halten.

    
Branko Dimitrijevic 16.11.2011 00:24
quelle
1

Wenn die Hierarchie nicht oft geändert wird, können Sie für jeden Knoten eine ganze Liste von Elementen berechnen (anstatt nur direkte Kinder). Auf diese Weise benötigen Sie deutlich mehr RAM, aber es funktioniert blitzschnell für jeden Benutzer, weil Sie die gesamte Liste der Nachkommenknoten in Single Read lesen können.

Für Ihr Beispiel (ich verwende das JSON-Format):

%Vor%

Nun, für Superuser müssen Sie immer noch viele Daten pro Anfrage übertragen, aber ich sehe keinen Weg, um es kleiner zu machen.

    
mephisto123 15.11.2011 23:54
quelle
0

Wir machen so etwas. Wir lesen den Baum in den Speicher, speichern ihn im Anwendungscache und greifen aus dem Speicher darauf zu. Da sich unsere Änderungen fast nie ändern und Änderungen nicht sofort in der Web-App reflektiert werden müssen, machen wir uns nicht einmal die Mühe, sie zu erkennen, sondern lassen den Cache altern und werden aktualisiert. Es funktioniert wirklich gut für uns.

    
drdwilcox 15.11.2011 23:42
quelle