Messenger-Datenbank (Teil 2): ​​Wir partitionieren "Profit"

Wir haben erfolgreich die Struktur unserer PostgreSQL-Datenbank für die Speicherung von Korrespondenz entworfen, ein Jahr ist vergangen, Benutzer füllen sie aktiv, jetzt hat sie Millionen von Datensätzen und ... etwas begann sich zu verlangsamen.



Tatsache ist, dass mit dem Wachstum des Tischvolumens auch die „Tiefe“ der Indizes wächst - wenn auch logarithmisch. Mit der Zeit wird der Server jedoch gezwungen, viele Datenseiten zu verarbeiten, um dieselben Lese- / Schreibaufgaben wie zu Beginn auszuführen.

Hier hilft die Partitionierung .

Ich stelle fest, dass es hier nicht um das Sharding geht, dh die Verteilung von Daten zwischen verschiedenen Datenbanken oder Servern. Denn selbst wenn Sie die Daten auf mehrere Server aufteilen, können Sie das Problem des "Anschwellens" der Indizes im Laufe der Zeit nicht beseitigen. Wenn Sie es sich leisten können, jeden Tag einen neuen Server in Betrieb zu nehmen, liegen Ihre Probleme nicht mehr auf der Ebene einer bestimmten Datenbank.

Wir werden nicht spezifische Skripte für die Implementierung der Partitionierung "in Hardware" betrachten, sondern den Ansatz selbst - was und wie man "in Scheiben schneidet" und wozu ein solcher Wunsch führt.

Konzept


Wir definieren erneut unser Ziel: Wir möchten sicherstellen, dass die Anzahl der PostgreSQL-Daten, die während eines Lese- / Schreibvorgangs gelesen werden, heute, morgen und nach einem Jahr in etwa gleich bleibt.

Für alle chronologisch gesammelten Daten (Nachrichten, Dokumente, Protokolle, Archive, ...) ist die natürliche Wahl des Partitionsschlüssels das Datum / die Uhrzeit des Ereignisses . In unserem Fall ist ein solches Ereignis der Zeitpunkt, zu dem die Nachricht gesendet wurde .

Beachten Sie, dass Benutzer fast immer nur mit den „neuesten“ Daten arbeiten - sie lesen die neuesten Nachrichten, analysieren die neuesten Protokolle ... Nein, natürlich können sie in der Zeit weiter zurückscrollen, tun dies nur sehr selten.

Aus diesen Einschränkungen wird deutlich, dass die "täglichen" Abschnitte die beste Lösung für Nachrichten sind - schließlich liest unser Benutzer fast immer, was ihm "heute" oder "gestern" einfiel.

Wenn wir tagsüber fast nur einen Abschnitt schreiben und lesen, können wir Speicher und Festplatte noch effizienter nutzen, da alle Abschnittsindizes im Gegensatz zu den "großen und fett gedruckten" Abschnitten der gesamten Tabelle problemlos in den Arbeitsspeicher passen.

Schritt für Schritt


Im Allgemeinen klingt alles wie ein solider Gewinn. Und es ist erreichbar, aber dafür müssen wir uns anstrengen - denn die Entscheidung, eine der Einheiten zu unterteilen, führt dazu, dass sie "geschnitten" und damit verbunden werden müssen .

Nachricht, ihre Eigenschaften und Projektionen


Da wir uns entschlossen haben, Nachrichten nach Datum zu unterteilen, ist es sinnvoll, die Entitäten-Eigenschaften (angehängte Dateien, Mailinglisten) und auch nach dem Datum der Nachricht zu unterteilen.

Da eine unserer typischen Aufgaben nur das Anzeigen von Nachrichtenregistern (ungelesen, eingehend, alle) ist, ist es auch logisch, sie nach Nachrichtendatum in die Partitionierung einzutragen.


Fügen Sie allen Tabellen den Partitionsschlüssel (Datum der Nachricht) hinzu: Empfänger, Datei, Registrierungen. Sie können die Nachricht nicht selbst hinzufügen, sondern die vorhandene DateTime verwenden.

Themen


Da das Thema in mehrere Nachrichten unterteilt ist, ist es nicht möglich, es im selben Modell zu „schneiden“, man muss sich auf etwas anderes verlassen. In unserem Fall ist das Datum der ersten Nachricht in der Korrespondenz ideal - das ist der Moment der Erstellung des Themas selbst.


Fügen Sie den Partitionsschlüssel (Themendatum) zu allen Tabellen hinzu: Thema, Teilnehmer.

Aber jetzt haben wir zwei Probleme gleichzeitig:

  • In welchem ​​Bereich soll nach Beiträgen zum Thema gesucht werden?
  • in welchem ​​abschnitt soll nach einem thema aus einer nachricht gesucht werden?

Sie können natürlich weiterhin in allen Abschnitten suchen, aber es wird sehr traurig sein und all unsere Gewinne zunichte machen. Um zu wissen, wo genau wir suchen müssen, werden wir daher logische Verknüpfungen / Verweise auf Abschnitte erstellen:

  • Fügen Sie in der Nachricht ein Feld mit dem Datum des Themas hinzu
  • Fügen Sie dem Thema eine Reihe von Nachrichtendaten für diese Korrespondenz hinzu (Sie können eine separate Tabelle oder ein Array von Daten verwenden).



Da die Liste der Nachrichtendaten für jede einzelne Korrespondenz nur geringfügig geändert wird (schließlich fallen fast alle Nachrichten in die nächsten 1-2 Tage), werde ich auf diese Option näher eingehen.

Insgesamt hat die Struktur unserer Basis unter Berücksichtigung der Aufteilung folgende Form angenommen:

Tabellen: RU, wenn Sie Kyrillisch nicht mögen, ist es besser, sich die Namen der Tabellen / Felder nicht anzusehen
--     CREATE TABLE "_YYYYMMDD"( "" uuid PRIMARY KEY , "" uuid , "" date , "" uuid , "" --    timestamp , "" text ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid , "" uuid , PRIMARY KEY("", "") ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid PRIMARY KEY , "" uuid , "BLOB" uuid , "" text ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid , "" smallint , "" timestamp , "" uuid , PRIMARY KEY("", "", "") ); CREATE INDEX ON "_YYYYMMDD"("", "", "" DESC); --     CREATE TABLE "_YYYYMMDD"( "" date , "" uuid PRIMARY KEY , "" uuid , "" text ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid , "" uuid , PRIMARY KEY("", "") ); CREATE TABLE "_YYYYMMDD"( "" date , "" uuid PRIMARY KEY , "" date ); 


Sparen Sie einen schönen Cent


Wenn wir nicht die klassische Partitionierungsoption verwenden, die auf der Verteilung der Feldwerte basiert (über Trigger und Vererbung oder PARTITION BY), sondern "manuell" auf Anwendungsebene, können wir feststellen, dass der Wert des Partitionierungsschlüssels bereits im Namen der Tabelle selbst gespeichert ist.

Wenn Sie also über die Menge der gespeicherten Daten besorgt sind, können Sie diese "zusätzlichen" Felder entfernen und auf bestimmte Tabellen verweisen. In diesem Fall müssen zwar alle Muster aus mehreren Abschnitten bei der Antragsseite eingereicht werden.

Source: https://habr.com/ru/post/de483170/


All Articles