DB för messenger (del 2): sektionera "i realtid"

Vi har framgĂ„ngsrikt designat strukturen för vĂ„r PostgreSQL-databas för att lagra konversationer, ett Ă„r har gĂ„tt, anvĂ€ndarna fyller den aktivt, och den har redan miljontals poster, och
 nĂ„got har börjat hacka.

DB för messenger (del 2): sektionera "i realtid"
Det handlar om att Med ökad tabellvolym ökar Ă€ven 'djupet' pĂ„ indexen — Ă€ven om det Ă€r i en logaritmisk takt. Men över tid tvingar det servern att hantera mycket fler datorsidor, Ă€n i början.

HÀr kommer hjÀlp frÄn partitionering.

Jag vill notera att vi inte pratar om sharding, det vill sÀga fördelning av data mellan olika databaser eller servrar. För Àven om du delar upp data pÄ nÄgra servrar, kommer du ÀndÄ inte undan problemet med 'uppblÄsta' index över tid. Det Àr klart att om du har möjlighet att sÀtta in en ny server varje dag, sÄ kommer dina problem att ligga i en helt annan dimension Àn den specifika databasen.

Vi kommer att titta pĂ„ inte konkreta skript för att implementera partitionering 'i hĂ„rdvara', utan sjĂ€lva tillvĂ€gagĂ„ngssĂ€ttet — vad och hur vi bör 'skĂ€ra i bitar', och vad detta önskemĂ„l leder till.

Koncept

LÄt oss Äterigen definiera vÄrt mÄl: vi vill göra sÄ att mÀngden lÀsta PostgreSQL-data vid vilken lÀsning/skrivning som helst förblir ungefÀr densamma idag, imorgon och om ett Är.

För alla data som samlas kronologiskt (meddelanden, dokument, loggar, arkiv, 
) Ă€r det naturliga valet av partitioneringsnyckel datum/tid för hĂ€ndelsen. I vĂ„rt fall Ă€r hĂ€ndelsen tillfĂ€llet för att skicka meddelandet.

Vi noterar att anvĂ€ndarna nĂ€stan alltid endast arbetar med de 'senaste' data — lĂ€ser de senaste meddelandena, analyserar de senaste loggarna,
 Nej, sjĂ€lvklart kan de blĂ€ddra lĂ€ngre tillbaka i tiden, men de gör detta vĂ€ldigt sĂ€llan.

Av dessa begrĂ€nsningar blir det uppenbart att den optimala lösningen för meddelanden blir 'dagliga' sektioner — eftersom vĂ„r anvĂ€ndare nĂ€stan alltid kommer att lĂ€sa det som kom 'idag' eller 'i gĂ„r'.

Om vi under dagen skriver och lĂ€ser nĂ€stan enbart frĂ„n en sektion, ger det oss Ă€ven en mer effektiv anvĂ€ndning av minnet och disken — eftersom alla index i sektionen enkelt fĂ„r plats i minnet, till skillnad frĂ„n "stora och tjocka" över hela tabellen.

steg-för-steg

Allt som sagts ovan lĂ„ter som en enda stor vinst. Och det Ă€r uppnĂ„eligt, men för det mĂ„ste vi verkligen anstrĂ€nga oss — för att beslutet att sektionera en av enheterna krĂ€ver att vi "skĂ€r upp" relaterade.

Meddelandet, dess egenskaper och projektioner

Eftersom vi bestÀmde oss för att dela upp meddelanden efter datum, sÄ Àr det Àven rimligt att dela upp relaterade enheter-egenskaper (bilagor, lista över mottagare), och ocksÄ efter meddelandets datum.

Eftersom en av vÄra typiska uppgifter Àr att granska registren av meddelanden (olÀsta, mottagna, alla), sÄ Àr det ocksÄ logiskt att "dras in" i sektioneringen efter meddelandets datum.

DB för messenger (del 2): sektionera "i realtid"

Vi lÀgger till sektioneringsnyckeln (meddelandets datum) i alla tabeller: mottagare, fil, register. I sjÀlva meddelandet behöver vi inte lÀgga till detta, utan kan anvÀnda den befintliga DatumTid.

Ämnen

Eftersom temat Ă€r ett av flera meddelanden, sĂ„ gĂ„r det inte att "skĂ€ra" det i samma modell, vi mĂ„ste förlita oss pĂ„ nĂ„got annat. I vĂ„rt fall passar det perfekt med datumet för det första meddelandet i konversationen — det vill sĂ€ga ögonblicket av skapandet av just temat.

DB för messenger (del 2): sektionera "i realtid"

Vi lÀgger till sektioneringsnyckeln (temadatumet) i alla tabeller: tema, deltagare.

Men nu har vi plötsligt tvÄ problem:

  • i vilken sektion ska vi leta efter meddelanden om temat?
  • i vilken sektion ska vi leta efter temat frĂ„n meddelandet?

Vi kan naturligtvis fortsÀtta att söka i alla sektioner, men det skulle bli mycket trÄkigt och innebÀra att förlora alla vÄra vinster. DÀrför, för att veta exakt var vi ska leta, ska vi göra logiska lÀnkar/pekare till sektionerna:

  • i meddelandet lĂ€gger vi till ett fĂ€lt med temat datum
  • till temat lĂ€gger vi till ett set av meddelandedatum för denna konversation (kan vara en separat tabell eller en array av datum)

DB för messenger (del 2): sektionera "i realtid"

Eftersom modifieringarna av meddelandedatumlistan för varje enskild konversation kommer att vara fÄ (nÀstan alla meddelanden hamnar inom 1-2 angrÀnsande dagar), sÄ kommer jag att stanna vid just detta alternativ.

Sammanfattningsvis har strukturen av vÄr databas följande utseende med hÀnsyn till sektioneringen:

Tabeller: RU, om man har aversion mot kyrilliska bokstÀver i namn pÄ tabeller/fÀlt Àr det bÀttre att inte titta

-- sektioner efter meddelandedatum
CREATE TABLE "Meddelande_YYYYMMDD"(
  "Meddelande"
    uuid
      PRIMARY KEY
, "Ämne"
    uuid
, "DatumÄmne"
    date
, "Författare"
    uuid
, "DatumTid" -- anvÀnds som datum
    timestamp
, "Text"
    text
);

CREATE TABLE "Adressat_YYYYMMDD"(
  "DatumMeddelande"
    date
, "Meddelande"
    uuid
, "Person"
    uuid
, PRIMARY KEY("Meddelande", "Person")
);

CREATE TABLE "Fil_YYYYMMDD"(
  "DatumMeddelande"
    date
, "Fil"
    uuid
      PRIMARY KEY
, "Meddelande"
    uuid
, "BLOB"
    uuid
, "Namn"
    text
);

CREATE TABLE "RegisterMeddelanden_YYYYMMDD"(
  "DatumMeddelande"
    date
, "Ägare"
    uuid
, "TypRegister"
    smallint
, "DatumTid"
    timestamp
, "Meddelande"
    uuid
, PRIMARY KEY("Ägare", "TypRegister", "Meddelande")
);
CREATE INDEX ON "RegisterMeddelanden_YYYYMMDD"("Ägare", "TypRegister", "DatumTid" DESC);

-- sektioner efter Àmnesdatum
CREATE TABLE "Ämne_YYYYMMDD"(
  "DatumÄmne"
    date
, "Ämne"
    uuid
      PRIMARY KEY
, "Dokument"
    uuid
, "Titel"
    text
);

CREATE TABLE "DeltagareÄmne_YYYYMMDD"(
  "DatumÄmne"
    date
, "Ämne"
    uuid
, "Person"
    uuid
, PRIMARY KEY("Ämne", "Person")
);

CREATE TABLE "DatumMeddelandenÄmne_YYYYMMDD"(
  "DatumÄmne"
    date
, "Ämne"
    uuid
      PRIMARY KEY
, "Datum"
    date
);

Sparar en slant

Men om vi inte anvÀnder den klassiska versionen av partitionering baserat pÄ vÀrdefÀltets fördelning (genom triggers och arv eller PARTITION BY), utan "manuellt" pÄ applikationsnivÄ, kan vi mÀrka att vÀrdet av partitioneringsnyckeln redan lagras i tabellens namn.

SÄ om du Àr sÄ oroad över mÀngden lagrade data, kan vi bli av med dessa "överflödiga" fÀlt och adressera specifika tabeller. Dock kommer alla urval frÄn flera sektioner i detta fall att behöva flyttas till applikationen.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster