Wie man GeschĂ€ftsanforderungen in konkrete Datenstrukturen ĂŒbertrĂ€gt, am Beispiel der Gestaltung einer Datenbank fĂŒr einen Messenger âvon Grund aufâ.
- Teil 1: Gestalten des DatenbankgerĂŒsts

Unsere Datenbank wird nicht so umfangreich und verteilt sein, oder , 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 eingebettet. 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 ohne Komplikationen durch UML oder IDEF-Notationen:

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 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.

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 in die Struktur unserer Datenbank gehen.
Quelle: habr.com
