Hur man kan översÀtta affÀrskrav till specifika datastrukturer med exemplet att designa en databas för en meddelandetjÀnst "frÄn grunden".
- Del 1: designa databasen

VĂ„r databas kommer inte att vara lika stor och distribuerad, eller , utan "bara för att ha", men det ska vara bra â funktionellt, snabbt och fĂ„ plats pĂ„ en server. PostgreSQL â sĂ„ att man kan köra en separat instans av tjĂ€nsten nĂ„gonstans pĂ„ sidan, till exempel.
DÀrför kommer vi inte att beröra frÄgor om sharding, replikering och geografiskt distribuerade system, utan fokusera pÄ schematiska lösningar inom databasen.
Steg 1: Lite affÀrsspecifika uppgifter
Vi kommer att designa vÄr meddelandetjÀnst inte abstrakt, utan integrera den i omgivningen . Det vill sÀga att mÀnniskor inte bara "chattar", utan kommunicerar med varandra i kontext av att lösa vissa affÀrsproblem.
Och vilka uppgifter finns det i affĂ€rsvĂ€rlden? LĂ„t oss titta pĂ„ exemplet med Vasily â chefen för utvecklingsavdelningen.
- "Nikolaj, en patch behövs för den hÀr uppgiften idag!"
Det betyder att kommunikationen kan ske i kontext av nÄgon Inkomststrukturen för PhD-studenter och Post-Docs pÄ EPFL. - "Kola, ska vi spela Dota ikvÀll?"
Det vill sÀga Àven en enda konversation kan pÄgÄ om olika Àmnen.. - "Petr, Nikolaj, kolla i bilagan för priset pÄ den nya servern."
SÄ, ett meddelande kan ha flera mottagare.Samtidigt kan ett meddelande innehÄlla bifogade filer.. - "Semen, och du kolla ocksÄ."
Och det ska finnas en möjlighet att bjuda in en ny deltagare till en redan existerande konversation..
LÄt oss stanna vid denna lista av "uppenbara" behov för tillfÀllet.
Utan förstÄelse för tillÀmpningens specifika uppgifter och givna begrÀnsningar Àr det praktiskt taget omöjligt att designa en effektiv databasstruktur för dess lösning.
Steg 2: Minimal logisk struktur
Strukturellt liknar det fortfarande ett e-postkonversation â ett traditionellt verktyg för att driva affĂ€rer. SĂ„ ja, "algoritmiskt" liknar mĂ„nga affĂ€rsproblem varandra, och dĂ€rför kommer Ă€ven verktygen för att lösa dem att vara strukturellt liknande.
LÄt oss dokumentera den logiska strukturen av relationerna mellan enheterna som vi har fÄtt. För att underlÀtta förstÄelsen av vÄr modell kommer vi att anvÀnda den mest primitiva varianten av att visa utan kompliceringar av UML eller IDEF-notation:

I vÄrt exempel Àr persona, dokumentet och den binÀra "kroppen" av filen - dessa Àr "externa" enheter som existerar pÄ egen hand, oberoende av vÄr service. SÄ vi kommer helt enkelt att betrakta dem som nÄgra lÀnkar "nÄgonstans" via UUID.
Rita scheman sĂ„ enkelt som möjligt â de flesta av dem du visar dem för Ă€r inte experter pĂ„ att lĂ€sa UML/IDEF. Men â rita absolut.
Steg 3: Skissa ut tabellstrukturen
Om tabell- och fÀltnamnMan kan ha olika Äsikter om "ryska" namn pÄ fÀlt och tabeller, men det Àr en smakfrÄga. Eftersom inte har utlÀndska utvecklare, och PostgreSQL tillÄter oss att anvÀnda namn som kan vara hieroglyfer, om de Àr omgivna av citattecken, sÄ föredrar vi att namnge objekt tydligt för att undvika missförstÄnd.
Eftersom mĂ„nga personer skriver meddelanden samtidigt, kan en del av dem faktiskt göra detta i offline-lĂ€ge, sĂ„ det enklaste alternativet Ă€r â att anvĂ€nda UUID som identifierare inte bara för externa enheter, utan Ă€ven för alla objekt inom vĂ„r tjĂ€nst. Dessutom kan de genereras Ă€ven pĂ„ klientsidan â detta hjĂ€lper oss att stödja sĂ€ndningen av meddelanden vid kortvarigt otillgĂ€nglighet av databasen, och risken för kollisioner Ă€r extremt lĂ„g.
Den preliminÀra tabellstrukturen i vÄr databas kommer att se ut sÄ hÀr:
Tabeller : RU
CREATE TABLE "Tema"(
"Tema"
uuid
PRIMARY KEY
, "Dokument"
uuid
, "Titel"
text
);
CREATE TABLE "Meddelande"(
"Meddelande"
uuid
PRIMARY KEY
, "Tema"
uuid
, "Författare"
uuid
, "DatumTid"
timestamp
, "Text"
text
);
CREATE TABLE "Adressat"(
"Meddelande"
uuid
, "Persona"
uuid
, PRIMARY KEY("Meddelande", "Persona")
);
CREATE TABLE "Fil"(
"Fil"
uuid
PRIMARY KEY
, "Meddelande"
uuid
, "BLOB"
uuid
, "Namn"
text
);Tabeller : 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
);Det enklaste nÀr man beskriver formatet Àr att börja "utveckla" relationen frÄn tabeller som inte refererar till nÄgon annan.
Steg 4: FörstÄ de otydliga behoven
Det Àr klart, vi har designat en databas som man kan skriva bra i och pÄ nÄgot sÀtt lÀsa.
LĂ„t oss sĂ€tta oss i stĂ€llet för anvĂ€ndaren av vĂ„r tjĂ€nst â vad skulle vi vilja göra med hjĂ€lp av den?
- Senaste meddelandena
Detta chronologiskt sorterade en register över "mina" meddelanden dÀr jag Àr mottagare, dÀr jag Àr avsÀndare, dÀr jag har fÄtt ett meddelande men inte svarat, dÀr jag inte fÄtt nÄgot svar, ⊠- Deltagare i konversationen
Vem deltar överhuvudtaget i den hÀr lÄnga chatten?
VĂ„r struktur kan lösa bĂ„da dessa uppgifter "övergripande", men snabbt â nej. Problemet Ă€r att för att sortera i den första uppgiften Ă€r det omöjligt att skapa ett index, som passar för varje deltagare (och vi mĂ„ste hĂ€mta alla poster), och för att lösa den andra krĂ€vs det att hĂ€mta alla meddelanden i Ă€mnet.
Oförutsedda anvÀndaruppgifter kan sÀtta en djup brun fÀrg pÄ prestandan.
Steg 5: Förnuftig denormalisering
BÄda vÄra problem kan lösas med hjÀlp av extra tabeller dÀr vi kommer att duplicera en del av de data, som behövs för att skapa lÀmpliga index för vÄra uppgifter.

Tabeller : RU
CREATE TABLE "MeddelandeRegister"(
"Ăgare"
uuid
, "RegisterTyp"
smallint
, "DatumTid"
timestamp
, "Meddelande"
uuid
, PRIMARY KEY("Ăgare", "RegisterTyp", "Meddelande")
);
CREATE INDEX ON "MeddelandeRegister"("Ăgare", "RegisterTyp", "DatumTid" DESC);
CREATE TABLE "TemaDeltagare"(
"Tema"
uuid
, "Person"
uuid
, PRIMARY KEY("Tema", "Person")
);Tabeller : 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)
);HÀr har vi anvÀnt tvÄ typiska tillvÀgagÄngssÀtt vid skapandet av hjÀlptabeller:
- Multiplikation av poster
Vi skapar för varje ursprunglig meddelandepost flera följdposter i olika typer av register för olika Ă€gare â bĂ„de för avsĂ€ndaren och för mottagaren. Men nu ligger varje register pĂ„ ett index â eftersom vi typiskt sett vill se bara första sidan. - Unikalisering av poster
Vid varje meddelande som skickas inom ett specifikt Ă€mne Ă€r det tillrĂ€ckligt att kontrollera om en sĂ„dan post redan existerar. Om inte â lĂ€gger vi till den i vĂ„r "ordbok".
I nÀsta del av artikeln kommer vi att prata om i strukturen av vÄr databas.
KĂ€lla: habr.com
