Messenger baza podataka (2. dio): particioniranje "za profit"
Uspješno smo dizajnirali strukturu naše PostgreSQL baze podataka za pohranjivanje korespondencije, prošla je godina dana, korisnici je aktivno popunjavaju, a sada milioni zapisa, i ... nešto je počelo sve usporavati.
Stvar je u tome sa rastom obima tabele, raste i „dubina“ indeksa - iako logaritamski. Ali s vremenom to prisiljava server da radi iste zadatke čitanja/pisanja obrađuju mnogo puta više stranica sa podacimanego na početku.
Tu dolazi do spašavanja sekcija.
Napominjem da se ne radi o shardingu, odnosno distribuciji podataka između različitih baza podataka ili servera. Jer čak i dijeljenje podataka sa nekoliko servera, nećete se riješiti problema "nabujanja" indeksa tokom vremena. Jasno je da ako si možete priuštiti stavljanje novog servera u rad svaki dan, onda vaši problemi više neće ležati na nivou određene baze podataka.
Nećemo razmatrati specifične skripte za implementaciju particioniranja "u hardveru", već sam pristup - šta i kako treba "isjeći na kriške" i čemu takva želja vodi.
Koncept
Još jednom definišemo naš cilj: želimo da budemo sigurni da danas, sutra i godinu dana kasnije, količina podataka koje PostgreSQL čita za bilo koju operaciju čitanja/pisanja ostaje približno ista.
Za bilo koje hronološki akumulirani podaci (poruke, dokumenti, evidencije, arhive,...) prirodan izbor za particioni ključ je datum/vrijeme događaja. U našem slučaju, ovaj događaj jeste u trenutku kada je poruka poslata.
Imajte na umu da korisnici gotovo uvijek raditi samo sa najnovijim takvi podaci - čitaju najnovije poruke, analiziraju najnovije logove,... Ne, naravno, mogu se pomicati i dalje u prošlost, samo što to rade vrlo rijetko.
Iz ovih ograničenja postaje očigledno da će optimalno rješenje za poruke biti "dnevne" sekcije - uostalom, gotovo uvijek će naš korisnik pročitati ono što mu je stiglo "danas" ili "juče".
Ako u toku dana pišemo i čitamo praktično samo u jednom dijelu, onda nam i to daje efikasnije korištenje memorije i diska - pošto se svi indeksi sekcija lako uklapaju u RAM, za razliku od onih "velikih i debelih" u tabeli.
korak po korak
Generalno, sve navedeno zvuči kao jedan veliki profit. I to je ostvarivo, ali za to ćemo se morati potruditi – jer odluka o podjeli jednog od entiteta dovodi do potrebe da se "pile" i pridruže.
Poruka, njena svojstva i projekcije
Budući da smo odlučili da smanjimo poruke po datumima, razumno je podijeliti i svojstva entiteta koja zavise od njih (priložene datoteke, lista primatelja) i takođe po datumu posta.
Budući da je jedan od naših tipičnih zadataka samo pregledavanje registara poruka (nepročitane, dolazne, sve), logično je i da ih "uvučemo" u sekcije prema datumima poruke.
Dodamo ključ particije (datum poruke) u sve tabele: primaoce, fajl, registre. Ne možete dodati samoj poruci, već koristiti postojeći DateTime.
teme
S obzirom da je tema jedna za više poruka, neće se moći „prerezati“ u istom modelu, potrebno je osloniti se na nešto drugo. Savršeno za naš slučaj. datum prve poruke u prepisci - odnosno trenutak stvaranja, zapravo, teme.
Dodajte ključ particije (datum predmeta) svim tablicama: tema, član.
Ali sada imamo dva problema odjednom:
u kojem dijelu tražiti poruke na temu?
u kojem dijelu tražiti predmet poruke?
Možete, naravno, nastaviti da tražite u svim odjeljcima, ali to će biti jako tužno i poništit će sve naše dobitke. Stoga, da bismo znali gdje tačno tražiti, napravićemo logičke veze / pokazivače na sekcije:
dodati u poruku polje datuma teme
dodati temu datum poruke je postavljen ova korespondencija (možete koristiti zasebnu tabelu ili možete koristiti niz datuma)
Budući da će biti nekoliko izmjena na listi datuma poruka za svaku pojedinačnu korespondenciju (na kraju krajeva, skoro sve poruke padaju na 1-2 susedna dana), fokusiraću se na ovu opciju.
Ukupno, struktura naše baze podataka je imala sljedeći oblik, uzimajući u obzir particioniranje:
Tablice : RU
-- секции по дате сообщения
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
);
Uštedi prilično peni
Pa, šta ako koristimo klasično sečenje. na osnovu distribucije vrijednosti polja (preko okidača i nasljeđivanja ili PARTITION BY), ali "ručno" na razini aplikacije, primijetit ćete da je vrijednost particionog ključa već pohranjena u nazivu same tablice.
Pa ako jesi veoma su zabrinuti zbog količine pohranjenih podataka, tada se možete riješiti ovih "dodatnih" polja i obratiti se posebno određenim tablicama. Istina, svi odabiri iz nekoliko odjeljaka u ovom slučaju već će se morati prenijeti na stranu aplikacije.