it-swarm.com.de

Wie entwerfe ich eine Datenbank zum Speichern einer sortierten Liste?

Ich möchte eine sortierte Liste in einer Datenbank speichern. Ich möchte die folgenden Operationen effizient ausführen.

  1. Einfügen (x) - Datensatz x in die Tabelle einfügen
  2. Löschen (x) - Löscht Datensatz x aus der Tabelle
  3. Vor (x, n) - Gibt die 'n' Datensätze vor dem Datensatz x in der sortierten Liste zurück.
  4. Nach (x, n) - Gibt die 'n' Datensätze zurück, die auf den Datensatz x in der sortierten Liste folgen.
  5. First (n) - Gibt die ersten 'n' Datensätze aus der sortierten Liste zurück.
  6. Last (n) - Gibt die letzten 'n' Datensätze aus der sortierten Liste zurück.
  7. Vergleiche (x, y) - Wenn zwei Datensätze x und y aus der Tabelle stammen, finde heraus, ob x> y ist.

Die einfache Methode, die ich mir vorstellen kann, besteht darin, eine Art 'Rang'-Attribut in der Tabelle zu speichern und durch Sortieren nach diesem Attribut abzufragen. Bei dieser Methode wird das Einfügen/Ändern eines Datensatzes mit einem Rang jedoch zu einer kostspieligen Operation. Gibt es eine bessere Methode?

Insbesondere möchte ich die Tabelle mit Amazon SimpleDB implementieren. Eine allgemeine Antwort für eine relationale Datenbank sollte aber auch hilfreich sein.

Update zum Lastprofil:

Da ich dies für eine Webanwendung plane, hängt dies von der Anzahl der Benutzer ab, die die App verwenden.

Wenn es 100.000 aktive Benutzer gibt (Superoptimismus: P), dann wäre meine sehr ungefähre Schätzung pro Tag

500.000 Auswahlen, 100.000 Einfügungen und Löschungen, 500.000 Aktualisierungen

Ich würde erwarten, dass der Tisch insgesamt auf 500.000 wächst.

Ich möchte die Aktualisierungen, Einfügungen und Vergleichsvorgänge optimieren. Der Rang der Gegenstände wird sich ständig ändern und ich muss die Tabelle auf dem neuesten Stand halten.

44
chitti

Wenn der Rang nicht völlig willkürlich ist, sondern von einer anderen Eigenschaft (z. B. Name, Spielerwertung usw.) abgeleitet werden kann, schauen Sie sich Joels Antwort genau an.

Wenn es eine willkürliche Eigenschaft Ihrer Daten ist , sollte dies als Spalte in Ihrer Datensatztabelle gespeichert werden. Angenommen, Amazon SimpleDB ähnelt dem typischen RDBMS, können Sie diese Spalte indizieren und alle oben genannten Fragen schnell mit der entsprechenden Indizierungsstrategie beantworten. Dies ist normal für ein RDBMS.

Da Sie eine hohe Einfüge- und Aktualisierungsaktivität, aber auch eine relativ hohe Leseaktivität erwarten, empfehle ich Folgendes:

  • Gruppieren Sie die Tabelle nach Rang, insbesondere wenn die überwiegende Mehrheit Ihrer Anfragen gegen Rang ist. Wenn dies nicht der Fall ist oder wenn die Auswahl eines Clusterschlüssels in SimpleDB nicht verfügbar ist, erstellen Sie einfach einen Index mit dem Rang als führende Spalte. Dies würde die Fragen 3-6 erfüllen.
  • Ein Index für den Datensatz zuerst und dann für den Rang (oder in der SQL Server-Welt nur für den Datensatz und den Rang INCLUDEing oder nur für den Datensatz, wenn Sie den Rang geclustert haben) würde Abfrage 7 erfüllen.
  • Die Vorgänge 1 und 2 können optimiert werden, indem Sie Ihre Daten entsprechend voneinander trennen (d. H. Das FILLFACTOR in SQL Server festlegen). Dies ist besonders wichtig, wenn Sie nach Rang gruppieren.
  • Behalten Sie beim Einfügen oder Aktualisieren von Rängen so viel Abstand wie möglich zwischen den Rangnummern bei, um die Möglichkeit zu minimieren, dass Sie einen vorhandenen Datensatz neu einstufen müssen, um eine Rangeinfügung oder -aktualisierung zu ermöglichen. Wenn Sie beispielsweise Ihre Datensätze in Schritten von 1000 einordnen, lassen Sie genügend Platz für etwa die Hälfte der Änderungen und Einfügungen mit minimaler Wahrscheinlichkeit, um einen Datensatz neu zu bewerten, der nicht direkt an diesen Änderungen beteiligt ist.
  • Ordnen Sie jede Nacht alle Datensätze neu an, um die Ranglücken zwischen ihnen zurückzusetzen.
  • Sie können die Häufigkeit der Massen-Neueinstufungen sowie die Ranglückengröße anpassen, um Ihre erwartete Anzahl von Einfügungen oder Aktualisierungen im Verhältnis zur Anzahl der vorhandenen Datensätze zu berücksichtigen. Wenn Sie also 100.000 Datensätze haben und erwarten, dass Ihre Einfügungen und Aktualisierungen 10% davon ausmachen, lassen Sie genügend Platz für 10.000 neue Ränge und ordnen Sie jeden Abend neu.
  • Das erneute Einordnen von 500.000 Datensätzen ist eine teure Operation, aber einmal am Tag oder in der Woche außerhalb der Geschäftszeiten sollte für eine solche Datenbank in Ordnung sein. Diese Massen-Neueinstufung außerhalb der Geschäftszeiten, um die Ranglücken beizubehalten, erspart Ihnen die Neueinstufung vieler Datensätze für jede Rangaktualisierung oder -einfügung während Ihrer normalen und Spitzenzeiten.

Wenn Sie mehr als 100.000 Lesevorgänge für eine Tabelle mit einer Größe von mehr als 100.000 erwarten, empfehle ich nicht, den Ansatz der verknüpften Liste zu verwenden. Es wird nicht gut auf diese Größen skaliert.

22
Nick Chammas

Ich verwende im Allgemeinen die von Ihnen beschriebene "Rang" -Methode. Anstatt mit dem Aktualisieren von Zeilen herumzuspielen, wenn Elemente neu angeordnet werden mussten, konnte ich oft alle Datensätze in der Liste löschen und neue Elemente in der richtigen Reihenfolge wieder einfügen. Diese Methode ist eindeutig für den Abruf optimiert.

Ein alternativer Ansatz wäre, die Datensätze als verknüpfte Liste zu modellieren, indem eine reflexive Fremdschlüsselspalte "Vorgänger" in der Tabelle verwendet wird:

ID   setID   item       predecessor
---  ------  ------     ------------
1    1       Apple      null
2    1       Orange     1
3    2       Cucumber   null
4    1       Pear       2
5    1       Grape      4
6    2       Carrot     3

Sie können problemlos eine Liste abrufen und Elemente mit geringem Aufwand hinzufügen und entfernen. Das Herausholen der Datensätze in der richtigen Reihenfolge ist jedoch schwierig. Vielleicht gibt es eine clevere Möglichkeit, dies in einer einzigen Abfrage zu tun, wahrscheinlich mit vielen Alias-Tabellenverknüpfungen.

Ich verwende diesen letzteren Ansatz häufig, wenn ich eine Beziehung im Baumstil modelliere (Kategorien, Ordner, Mengen und Teilmengen). Ich hatte im Allgemeinen eine rekursive Funktion, um den vollständigen Baum in meiner Anwendung zu rekonstruieren.

13
bpanulla

Ich würde denken, die Sache zu tun ist, speichern Sie die Eigenschaft oder Eigenschaften, die zur Berechnung des Ranges verwendet werden und erstellen Sie dann einen Index über sie. Anstatt zu versuchen, die Datenbank zu zwingen, die Daten physisch in Rangfolge zu speichern, oder eine manuell verwaltete verknüpfte Liste zu verwenden, lassen Sie das Datenbankmodul das tun, wofür es entwickelt wurde.

6
Joel Brown

Dies sind die Einschränkungen eines Nicht-RDBMS wie simpleDB. Die von Ihnen benötigten Funktionen können in simpleDB nicht auf der DB-Seite implementiert werden, sondern müssen von der Programmierseite/Anwendung aus implementiert werden.

Für ein RDBMS wie SQL server sind die Funktionen, die Sie benötigen, für den Clustered-Index rudimentär.

  • Einfügen (x) - Fügen Sie Datensatz x in die Tabelle ein> Einfaches Einfügen.
  • Löschen (x) - Datensatz x aus der Tabelle löschen> Einfach löschen.
  • Vor (x, n) - Gibt die 'n' Datensätze vor dem Datensatz x in der sortierten Liste zurück. > Wählen Sie die besten n Ergebnisse aus, bei denen x kleiner als der Wert ist, und ordnen Sie nach Klausel.

  • Nach (x, n) - Gibt die 'n' Datensätze zurück, die auf den Datensatz x in der sortierten Liste folgen. > Wählen Sie die besten n Ergebnisse aus, wobei x größer als der Wert und die Reihenfolge nach Klausel ist.

  • First (n) - Gibt die ersten 'n' Datensätze aus der sortierten Liste zurück. > Wählen Sie die besten n Ergebnisse aus.

  • Last (n) - Gibt die letzten 'n' Datensätze aus der sortierten Liste zurück. > Wählen Sie die besten n Ergebnisse nach der Bestellung nach absteigend aus.

  • Vergleiche (x, y) - Wenn zwei Datensätze x und y aus der Tabelle stammen, finde heraus, ob x> y ist. > TSQL IF-Anweisung.
1
StanleyJohns

Folgendes habe ich verwendet, um meine Postgres-Tabelle nach jeder Einfügung neu zu ordnen:

CREATE OR REPLACE FUNCTION re_rank_list() RETURNS trigger AS $re_rank_list$
DECLARE
    temprow record;
    row_idx integer := 1;    
BEGIN
    FOR temprow IN
    SELECT * FROM your_schema.your_list WHERE list_id = NEW.list_id ORDER BY rank ASC
    LOOP
        UPDATE your_schema.your_list SET rank = row_idx * 100 WHERE id = temprow.id;
        row_idx := row_idx + 1;
    END LOOP;
    RETURN NEW;
END;
$re_rank_list$ LANGUAGE plpgsql;


CREATE TRIGGER re_rank_list AFTER UPDATE ON your_schema.your_list_value
    FOR EACH ROW 
    WHEN (pg_trigger_depth() = 0)
    EXECUTE PROCEDURE re_rank_list();

Für meinen Anwendungsfall ist die Leistung kein Problem, aber das Vertrauen, dass sie niemals kaputt geht oder sich merkwürdig verhält, ist wichtig.

0
Mark