Messenger-Datenbank (Teil 2): ​​Partitionierung „für Profit“

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

Messenger-Datenbank (Teil 2): ​​Partitionierung „für Profit“
Tatsache ist, dass Mit dem Wachstum des Tabellenvolumens wächst auch die „Tiefe“ der Indizes - wenn auch logarithmisch. Mit der Zeit wird der Server jedoch gezwungen, dieselben Lese-/Schreibaufgaben auszuführen ein Vielfaches mehr Datenseiten verarbeitenals am Anfang.

Hier kommt es zur Rettung Aufteilung.

Ich stelle fest, dass es hier nicht um Sharding geht, also um die Verteilung von Daten zwischen verschiedenen Datenbanken oder Servern. Denn sogar die Daten dividieren durch mehrere Servern werden Sie das Problem der „anschwellenden“ Indizes mit der Zeit nicht los. Es ist klar: 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 betrachten nicht spezifische Skripte zur Implementierung der Partitionierung „in Hardware“, sondern den Ansatz selbst – was und wie „in Scheiben geschnitten“ werden soll und wozu ein solcher Wunsch führt.

Konzept

Wir definieren noch einmal unser Ziel: Wir wollen sicherstellen, dass heute, morgen und ein Jahr später die von PostgreSQL gelesene Datenmenge für jeden Lese-/Schreibvorgang ungefähr gleich bleibt.

Für jeden chronologisch akkumulierte Daten (Nachrichten, Dokumente, Protokolle, Archive, ...) Die natürliche Wahl für einen Partitionsschlüssel ist Datum/Uhrzeit der Veranstaltung. In unserem Fall ist dieses Ereignis in dem Moment, in dem die Nachricht gesendet wurde.

Beachten Sie, dass Benutzer fast immer Arbeiten Sie nur mit der neuesten Version solche Daten - sie lesen die neuesten Nachrichten, analysieren die neuesten Protokolle, ... Nein, natürlich können sie weiter in der Zeit zurückscrollen, nur tun sie das sehr selten.

Aus diesen Einschränkungen wird deutlich, dass die optimale Lösung für Nachrichten sein wird „tägliche“ Abschnitte - schließlich wird unser Benutzer fast immer lesen, was ihm „heute“ oder „gestern“ eingefallen ist.

Wenn wir tagsüber praktisch nur in einem Abschnitt schreiben und lesen, dann gibt uns das auch effizientere Speicher- und Festplattennutzung - da alle Abschnittsindizes problemlos in den RAM passen, im Gegensatz zu den „großen und dicken“ in der gesamten Tabelle.

Schritt für Schritt

Im Allgemeinen klingt alles nach einem großen Gewinn. Und es ist erreichbar, aber dafür müssen wir hart arbeiten – denn Die Entscheidung, eine der Einheiten aufzuteilen, führt zu der Notwendigkeit, sie zu „sägen“ und zu verbinden.

Nachricht, ihre Eigenschaften und Projektionen

Da wir uns entschieden haben, Nachrichten nach Datum zu kürzen, ist es auch sinnvoll, die davon abhängigen Entitätseigenschaften (angehängte Dateien, Empfängerliste) zu unterteilen auch nach Postdatum.

Da eine unserer typischen Aufgaben lediglich darin besteht, die Nachrichtenregister (ungelesen, eingehend, alle) anzuzeigen, ist es auch logisch, sie nach Nachrichtendatum aufzuteilen.

Messenger-Datenbank (Teil 2): ​​Partitionierung „für Profit“

Wir fügen den Partitionsschlüssel (Nachrichtendatum) zu allen Tabellen hinzu: Empfänger, Datei, Register. Sie können nichts zur Nachricht selbst hinzufügen, sondern die vorhandene DateTime verwenden.

Themen

Da es sich bei dem Thema um ein Thema für mehrere Nachrichten handelt, ist es nicht möglich, es im selben Modell zu „schneiden“, sondern man muss sich auf etwas anderes verlassen. Perfekt für unseren Fall. Datum der ersten Nachricht in der Korrespondenz - das heißt, der Moment der eigentlichen Entstehung des Themas.

Messenger-Datenbank (Teil 2): ​​Partitionierung „für Profit“

Fügen Sie allen Tabellen einen Partitionsschlüssel (Betreffdatum) hinzu: Thema, Mitglied.

Aber jetzt haben wir gleich zwei Probleme:

  • In welchem ​​Abschnitt kann man nach Nachrichten zum Thema suchen?
  • In welchem ​​Abschnitt soll nach dem Betreff der Nachricht gesucht werden?

Sie können natürlich weiterhin in allen Bereichen suchen, aber das wäre sehr traurig und würde alle unsere Gewinne zunichte machen. Um genau zu wissen, wo Sie suchen müssen, werden wir daher logische Links / Verweise auf Abschnitte erstellen:

  • zur Nachricht hinzufügen Themen-Datumsfeld
  • zum Thema ergänzen Nachrichtendatum eingestellt diese Korrespondenz (Sie können eine separate Tabelle oder ein Array von Daten verwenden)

Messenger-Datenbank (Teil 2): ​​Partitionierung „für Profit“

Da sich die Liste der Nachrichtendaten für jede einzelne Korrespondenz kaum ändern wird (schließlich fallen fast alle Nachrichten auf 1-2 aufeinanderfolgende Tage), werde ich mich auf diese Option konzentrieren.

Insgesamt stellte sich der Aufbau unserer Datenbank unter Berücksichtigung der Partitionierung wie folgt dar:

Tische: RU

-- секции по дате сообщения
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 hübschen Cent

Nun, was ist, wenn wir verwenden klassische Aufteilung. Basierend auf der Verteilung der Feldwerte (durch Trigger und Vererbung oder PARTITION BY), aber „manuell“ auf Anwendungsebene, werden Sie feststellen, dass der Wert des Partitionierungsschlüssels bereits im Namen der Tabelle selbst gespeichert ist.

Also, wenn Sie es sind sind sehr besorgt über die Menge der gespeicherten Daten, dann können Sie diese „zusätzlichen“ Felder entfernen und gezielt auf bestimmte Tabellen zugreifen. Allerdings müssen in diesem Fall bereits alle Auswahlen aus mehreren Abschnitten auf die Anwendungsseite übertragen werden.

Source: habr.com

Kommentar hinzufügen