Datenbank des Messengers (Teil 1): Rahmen der Datenbank entwerfen

Wie man GeschĂ€ftsanforderungen in konkrete Datenstrukturen ĂŒbertrĂ€gt, am Beispiel der Gestaltung einer Datenbank fĂŒr einen Messenger „von Grund auf“.

Datenbank des Messengers (Teil 1): Rahmen der Datenbank entwerfen
Unsere Datenbank wird nicht so umfangreich und verteilt sein, wie bei VKontakte oder Badoo, sondern „nur damit es da ist“, doch es soll gut sein – funktional, schnell und auf einem Server Platz finden PostgreSQL – damit man einen separaten Dienst-Exemplar irgendwo an der Seite bereitstellen kann, zum Beispiel.

Deshalb werden wir die Fragen der Sharding, Replikation und geo-verteilten Systeme nicht behandeln, sondern uns auf schematische Lösungen innerhalb der DB konzentrieren.

Schritt 1: Etwas geschÀftsspezifisches

Unser Nachrichtenaustausch wird nicht abstrakt entworfen, sondern in die Umgebung der Unternehmens-Sozialen Netzwerkeeingebettet. Das heißt, die Menschen „unterhalten sich“ nicht einfach, sondern kommunizieren im Kontext der Lösung bestimmter geschĂ€ftlicher Aufgaben.

Und welche Aufgaben gibt es im GeschĂ€ft?.. Schauen wir uns das am Beispiel von Wladimir – dem Leiter der Entwicklungsabteilung – an.

  • „Nikolaj, fĂŒr diese Aufgabe wird das Patch schon heute benötigt!“
    Das heißt, die Korrespondenz kann im Kontext eines des Dokuments.
  • „Kola, lass am Abend in Dota gehen?“
    Das bedeutet, dass selbst bei einem Paar GesprĂ€chspartner die Kommunikation gleichzeitig ĂŒber verschiedene Themen gefĂŒhrt werden kann.
  • „Peter, Nikolaj, schaut euch im Anhang den Preis fĂŒr den neuen Server an.“
    So kann eine Nachricht mehrere EmpfÀnger haben.Dabei kann die Nachricht anhÀngende Dateien enthalten.
  • „Semen, schau du auch mal.“
    Und es muss möglich sein, einen neuen Teilnehmer in eine bereits bestehende Korrespondenz einzuladen..

Lassen Sie uns vorerst bei dieser Liste der „offensichtlichen“ BedĂŒrfnisse bleiben.

Ohne das VerstĂ€ndnis der anwendungsbezogenen Spezifik der Aufgabe und der damit verbundenen EinschrĂ€nkungen ist es praktisch unmöglich, eine effektive Datenbankschema fĂŒr ihre Lösung zu entwerfen.

Schritt 2: Minimales logisches Schema

Schematisch sieht es bisher sehr Ă€hnlich aus wie bei E-Mail-Korrespondenz – einem traditionellen GeschĂ€ftsinstrument. TatsĂ€chlich sind viele geschĂ€ftliche Aufgaben „algorithmisch“ einander Ă€hnlich, weshalb auch die Instrumente zu ihrer Lösung strukturell Ă€hnlich sein werden.

Lassen Sie uns das bereits erhaltene logische Schema der Beziehungen der EntitÀten festhalten. Zur Vereinfachung unserer Modellssicht verwenden wir die einfachste Variante der Darstellung ER-Modells ohne Komplikationen durch UML oder IDEF-Notationen:

Datenbank des Messengers (Teil 1): Rahmen der Datenbank entwerfen

In unserem Beispiel sind die Person, das Dokument und der binĂ€re „Körper“ der Datei externe EntitĂ€ten, die unabhĂ€ngig von unserem Dienst existieren. Daher werden wir sie in Zukunft einfach als Links „irgendwohin“ nach UUID betrachten.

Zeichnen Sie Diagramme so einfach wie möglich — die meisten von denen, denen Sie sie zeigen werden, sind keine Experten im Lesen von UML/IDEF. Aber — zeichnen Sie es auf jeden Fall.

Schritt 3: Skizzieren Sie die Struktur der Tabellen

Über die Namen von Tabellen und FeldernZu den „russischen“ Bezeichnungen von Feldern und Tabellen kann man unterschiedlich stehen, aber das ist Geschmackssache. Da wir in „Tensor“ keine auslĂ€ndischen Entwickler haben und PostgreSQL es uns erlaubt, Namen sogar mit Hieroglyphen zu geben, wenn sie in AnfĂŒhrungszeichen gesetzt sind, ziehen wir es vor, Objekte eindeutig und klar zu benennen, um MissverstĂ€ndnisse zu vermeiden.
Da viele Menschen gleichzeitig Nachrichten schreiben, können einige dies sogar im Offline-Modus, ist die einfachste Variante — UUIDs als Identifikatoren zu verwenden nicht nur fĂŒr externe EntitĂ€ten, sondern auch fĂŒr alle Objekte innerhalb unseres Dienstes. Außerdem können sie sogar auf der Client-Seite generiert werden — das hilft uns, den Versand von Nachrichten bei kurzfristiger NichtverfĂŒgbarkeit der Datenbank aufrechtzuerhalten, und die Wahrscheinlichkeit einer Kollision ist extrem gering.

Die grobe Struktur der Tabellen in unserer Datenbank wird folgendermaßen aussehen:
Tabellen : RU

CREATE TABLE "Thema"(
  "Thema"
    uuid
      PRIMARY KEY
, "Dokument"
    uuid
, "Titel"
    text
);

CREATE TABLE "Nachricht"(
  "Nachricht"
    uuid
      PRIMARY KEY
, "Thema"
    uuid
, "Autor"
    uuid
, "DatumUhrzeit"
    timestamp
, "Text"
    text
);

CREATE TABLE "Adressat"(
  "Nachricht"
    uuid
, "Person"
    uuid
, PRIMARY KEY("Nachricht", "Person")
);

CREATE TABLE "Datei"(
  "Datei"
    uuid
      PRIMARY KEY
, "Nachricht"
    uuid
, "BLOB"
    uuid
, "Name"
    text
);

Tabellen : EN

CREATE TABLE theme(
  theme
    uuid
      PRIMARY KEY
, document
    uuid
, title
    text
);

CREATE TABLE message(
  message
    uuid
      PRIMARY KEY
, theme
    uuid
, author
    uuid
, dt
    timestamp
, body
    text
);

CREATE TABLE message_addressee(
  message
    uuid
, person
    uuid
, PRIMARY KEY(message, person)
);

CREATE TABLE message_file(
  file
    uuid
      PRIMARY KEY
, message
    uuid
, content
    uuid
, filename
    text
);

Das Einfachste bei der Beschreibung des Formats ist, mit den „verknĂŒpften“ Beziehungen von Tabellen zu beginnen, die sich nicht auf niemanden beziehen.

Schritt 4: Unklarheiten ermitteln

Alles, wir haben eine Datenbank entworfen, in die man hervorragend schreiben kann und irgendwie lesen.

Lassen Sie uns in die Rolle des Nutzers unseres Dienstes schlĂŒpfen – was möchten wir mit ihm erreichen?

  • Letzte Nachrichten
    Das ist chronologisch sortiert nach verschiedenen Kriterien das Register „meiner“ Nachrichten. Wo ich einer der Adressaten bin, wo ich der Autor bin, wo mir geschrieben wurde, aber ich nicht geantwortet habe, wo ich keine Antwort erhalten habe, 

  • Teilnehmer der Korrespondenz
    Wer nimmt eigentlich an diesem langen, langen Chat teil?

Unsere Struktur ermöglicht es, beide Aufgaben â€žĂŒberhaupt“ zu lösen, aber schnell – nein. Das Problem ist, dass es fĂŒr die Sortierung im Rahmen der ersten Aufgabe unmöglich ist, einen Index zu erstellen,, der fĂŒr jeden der Teilnehmer geeignet ist (und wir mĂŒssen alle EintrĂ€ge abrufen), und zur Lösung der zweiten Aufgabe ist es notwendig, alle Nachrichten zum Thema abzurufen.

Unvorhergesehene Nutzeraufgaben können eine große Belastung fĂŒr die Leistung darstellen..

Schritt 5: Sinnvolle Denormalisierung

Beide unsere Probleme können mit zusĂ€tzlichen Tabellen gelöst werden, in denen wir einen Teil der Daten duplizieren, die fĂŒr die Bildung geeigneter Indizes fĂŒr unsere Aufgaben notwendig sind.
Datenbank des Messengers (Teil 1): Rahmen der Datenbank entwerfen

Tabellen : RU

CREATE TABLE "NachrichtenRegister"(
  "Besitzer"
    uuid
, "RegisterTyp"
    smallint
, "DatumZeit"
    timestamp
, "Nachricht"
    uuid
, PRIMARY KEY("Besitzer", "RegisterTyp", "Nachricht")
);
CREATE INDEX ON "NachrichtenRegister"("Besitzer", "RegisterTyp", "DatumZeit" DESC);

CREATE TABLE "ThemaTeilnehmer"(
  "Thema"
    uuid
, "Person"
    uuid
, PRIMARY KEY("Thema", "Person")
);

Tabellen : EN

CREATE TABLE message_registry(
  owner
    uuid
, registry
    smallint
, dt
    timestamp
, message
    uuid
, PRIMARY KEY(owner, registry, message)
);
CREATE INDEX ON message_registry(owner, registry, dt DESC);

CREATE TABLE theme_participant(
  theme
    uuid
, person
    uuid
, PRIMARY KEY(theme, person)
);

Hier haben wir zwei typische AnsÀtze angewendet, die bei der Erstellung von Hilfstabellen verwendet werden:

  • Multiplikation von EintrĂ€gen
    Wir erstellen aus einem einzelnen ursprĂŒnglichen Eintrag mehrere FolgeeintrĂ€ge in verschiedene Arten von Registern fĂŒr verschiedene EigentĂŒmer – sowohl fĂŒr den Absender als auch fĂŒr den EmpfĂ€nger. So hat nun jeder dieser Register einen Index – denn typischerweise möchten wir nur die erste Seite sehen.
  • Eindeutigkeit von EintrĂ€gen
    Bei jedem Versand einer Nachricht innerhalb eines bestimmten Themas genĂŒgt es zu ĂŒberprĂŒfen, ob ein solcher Eintrag bereits existiert. Wenn nicht – fĂŒgen wir ihn in unser „Wörterbuch“ ein.

Im nÀchsten Teil des Artikels wird es um die Implementierung von Partitionierung in die Struktur unserer Datenbank gehen.

Quelle: habr.com

60GB SSD 8Gb DDR4