Baza de date Messenger (partea 2): partiționare „pentru profit”

Am proiectat cu succes structura bazei noastre de date PostgreSQL pentru stocarea corespondenței, a trecut un an, utilizatorii o completează în mod activ, iar acum conține milioane de înregistrări, și... ceva a început să încetinească.

Baza de date Messenger (partea 2): partiționare „pentru profit”
Fapt este că Pe măsură ce dimensiunea tabelului crește, crește și „adâncimea” indicilor. - deși logaritmic. Dar, în timp, acest lucru forțează serverul să efectueze aceleași sarcini de citire/scriere procesează de multe ori mai multe pagini de datedecât la început.

Aici vine vorba de salvare secţionarea.

Permiteți-mi să observ că nu vorbim despre sharding, adică despre distribuirea datelor între diferite baze de date sau servere. Pentru că chiar împărțind datele în unele servere, nu veți scăpa de problema „umflarii” indicilor în timp. Este clar că dacă îți poți permite să pui în funcțiune un nou server în fiecare zi, atunci problemele tale nu vor mai sta deloc în planul unei anumite baze de date.

Vom lua în considerare nu scripturi specifice pentru implementarea partiționării „în hardware”, ci abordarea în sine - ce și cum ar trebui „tăiat în felii” și la ce duce o astfel de dorință.

Concept

Să ne definim încă o dată obiectivul: vrem să ne asigurăm că astăzi, mâine și peste un an, cantitatea de date citită de PostgreSQL în timpul oricărei operațiuni de citire/scriere rămâne aproximativ aceeași.

Pentru orice date acumulate cronologic (mesaje, documente, jurnale, arhive, ...) alegerea firească ca cheie de partiționare este data/ora evenimentului. În cazul nostru, un astfel de eveniment este momentul trimiterii mesajului.

Rețineți că utilizatorii aproape întotdeauna lucrați numai cu cele „ultime”. astfel de date - citesc cele mai recente mesaje, analizează cele mai recente jurnale,... Nu, desigur, pot derula mai departe în timp, dar fac asta foarte rar.

Din aceste constrângeri este clar că soluția optimă a mesajului ar fi secțiunile „zilnic”. - la urma urmei, utilizatorul nostru va citi aproape întotdeauna ceea ce i-a venit „azi” sau „ieri”.

Dacă scriem și citim aproape doar într-o singură secțiune în timpul zilei, atunci și asta ne oferă utilizarea mai eficientă a memoriei și a discului - deoarece toți indicii de secțiune se potrivesc cu ușurință în RAM, spre deosebire de cei „mari și grasi” din tabel.

pas cu pas

În general, tot ceea ce s-a spus mai sus sună ca un profit continuu. Și este realizabil, dar pentru asta va trebui să încercăm din greu - pentru că decizia de împărțire a uneia dintre entități conduce la necesitatea „văzării” asociatului.

Mesaj, proprietățile și proiecțiile sale

Deoarece am decis să tăiem mesajele după date, este logic să împărțim și entitățile-proprietăți care depind de ele (fișiere atașate, lista de destinatari) și de asemenea, după data mesajului.

Deoarece una dintre sarcinile noastre tipice este vizualizarea cu precizie a registrelor de mesaje (necitite, primite, toate), este de asemenea logic să le „atragem” în partiționarea după datele mesajelor.

Baza de date Messenger (partea 2): partiționare „pentru profit”

Adăugăm cheia de partiționare (data mesajului) la toate tabelele: destinatari, fișier, registre. Nu trebuie să îl adăugați la mesaj în sine, ci să utilizați DateTime existent.

Subiecte

Deoarece există un singur subiect pentru mai multe mesaje, nu există nicio modalitate de a-l „taia” în același model; trebuie să te bazezi pe altceva. In cazul nostru este ideal data primului mesaj în corespondență — adică momentul creării, de fapt, a subiectului.

Baza de date Messenger (partea 2): partiționare „pentru profit”

Adăugați cheia de partiționare (data subiectului) la toate tabelele: subiect, participant.

Dar acum avem două probleme deodată:

  • În ce secțiune ar trebui să caut mesaje pe această temă?
  • În ce secțiune ar trebui să caut subiectul din mesaj?

Putem, desigur, să continuăm să căutăm în toate secțiunile, dar acest lucru va fi foarte trist și va anula toate câștigurile noastre. Prin urmare, pentru a ști unde să căutam exact, vom face link-uri/pointeri logici către secțiuni:

  • vom adauga in mesaj câmp pentru data subiectului
  • hai sa adaugam la subiect data mesajului setată această corespondență (poate fi un tabel separat sau o serie de date)

Baza de date Messenger (partea 2): partiționare „pentru profit”

Deoarece vor exista puține modificări ale listei de date ale mesajelor pentru fiecare corespondență individuală (la urma urmei, aproape toate mesajele cad în 1-2 zile adiacente), mă voi concentra pe această opțiune.

În total, structura bazei noastre de date a luat următoarea formă, ținând cont de partiționare:

Tabele: RU, dacă aveți o aversiune față de alfabetul chirilic în numele tabelelor/câmpurilor, este mai bine să nu căutați

-- секции по дате сообщения
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
);

Economisiți un bănuț frumos

Ei bine, dacă nu folosim opțiunea clasică de secționare pe baza distribuției valorilor câmpurilor (prin declanșatoare și moștenire sau PARTITION BY) și „manual” la nivel de aplicație, veți observa că valoarea cheii de partiționare este deja stocată în numele tabelului în sine.

Deci, dacă ești așa Sunteți foarte îngrijorat de cantitatea de date stocată?, atunci puteți scăpa de aceste câmpuri „în plus” și puteți adresa anumite tabele. Adevărat, toate selecțiile din mai multe secțiuni în acest caz vor trebui transferate în partea aplicației.

Sursa: www.habr.com

Adauga un comentariu