Messenger database (bahagi 2): paghahati "para sa kita"

Matagumpay naming idinisenyo ang istruktura ng aming database ng PostgreSQL para sa pag-iimbak ng mga sulat, isang taon na ang lumipas, aktibong pinupunan ito ng mga user, at ngayon ay naglalaman na ito ng milyon-milyong mga rekord, at... may nagsimulang bumagal.

Messenger database (bahagi 2): paghahati "para sa kita"
Katunayan ay ang Habang lumalaki ang laki ng talahanayan, lumalaki din ang "lalim" ng mga index. - kahit na logarithmically. Ngunit sa paglipas ng panahon pinipilit nito ang server na gawin ang parehong mga gawain sa pagbasa/pagsusulat magproseso ng maraming beses na higit pang mga pahina ng datakaysa sa simula.

Ito ay kung saan ito dumating sa pagliligtas sectioning.

Hayaan akong tandaan na hindi namin pinag-uusapan ang tungkol sa sharding, iyon ay, pamamahagi ng data sa pagitan ng iba't ibang mga database o server. Dahil kahit hatiin ang data sa ilan mga server, hindi mo maaalis ang problema ng mga index na "pamamaga" sa paglipas ng panahon. Malinaw na kung kaya mong maglagay ng bagong server sa pagpapatakbo araw-araw, ang iyong mga problema ay hindi na magsisinungaling sa eroplano ng isang tiyak na database.

Isasaalang-alang namin hindi ang mga tiyak na script para sa pagpapatupad ng partitioning "sa hardware", ngunit ang diskarte mismo - kung ano at paano dapat "hiwain sa mga hiwa", at kung ano ang hahantong sa gayong pagnanais.

Konsepto

Muli nating tukuyin ang aming layunin: gusto naming tiyakin na ngayon, bukas, at sa isang taon, ang dami ng data na nabasa ng PostgreSQL sa anumang operasyon ng read/write ay nananatiling halos pareho.

Para sa anumang kronolohikal na naipon na data (mga mensahe, dokumento, log, archive, ...) ang natural na pagpipilian bilang isang partitioning key ay petsa/oras ng kaganapan. Sa aming kaso, ang ganitong kaganapan ay sandali ng pagpapadala ng mensahe.

Tandaan na ang mga gumagamit ay halos palaging gumana lamang sa mga "pinakabago". naturang data - binabasa nila ang pinakabagong mga mensahe, pinag-aaralan ang pinakabagong mga log,... Hindi, siyempre, maaari silang mag-scroll nang higit pa pabalik sa nakaraan, ngunit bihira nilang gawin ito.

Mula sa mga hadlang na ito ay malinaw na ang pinakamainam na solusyon sa mensahe ay magiging "araw-araw" na mga seksyon - pagkatapos ng lahat, ang aming gumagamit ay halos palaging magbabasa kung ano ang dumating sa kanya "ngayon" o "kahapon".

Kung nagsusulat at nagbabasa tayo halos sa isang seksyon lamang sa araw, ito rin ay nagbibigay sa atin mas mahusay na paggamit ng memorya at disk - dahil ang lahat ng mga index ng seksyon ay madaling magkasya sa RAM, sa kaibahan sa mga "malaki at mataba" sa buong talahanayan.

hakbang-hakbang

Sa pangkalahatan, ang lahat ng sinabi sa itaas ay parang isang tuluy-tuloy na kita. At ito ay makakamit, ngunit para dito kailangan nating magsikap - dahil ang desisyon na hatiin ang isa sa mga entidad ay humahantong sa pangangailangan na "nakita" ang nauugnay.

Mensahe, mga katangian nito at mga projection

Dahil nagpasya kaming i-cut ang mga mensahe ayon sa mga petsa, makatuwirang hatiin din ang mga entity-properties na umaasa sa kanila (naka-attach na mga file, listahan ng mga tatanggap), at ayon din sa petsa ng mensahe.

Dahil ang isa sa aming mga tipikal na gawain ay ang tumpak na pagtingin sa mga rehistro ng mensahe (hindi pa nababasa, papasok, lahat), lohikal din na "ilagay ang mga ito" sa paghahati ayon sa mga petsa ng mensahe.

Messenger database (bahagi 2): paghahati "para sa kita"

Idinaragdag namin ang partitioning key (petsa ng mensahe) sa lahat ng mga talahanayan: mga tatanggap, file, mga rehistro. Hindi mo kailangang idagdag ito sa mismong mensahe, ngunit gamitin ang umiiral na DateTime.

threads

Dahil mayroon lamang isang paksa para sa ilang mga mensahe, walang paraan upang "i-cut" ito sa parehong modelo; kailangan mong umasa sa ibang bagay. Sa aming kaso ito ay perpekto petsa ng unang mensahe sa sulat β€” iyon ay, ang sandali ng paglikha, sa katunayan, ng paksa.

Messenger database (bahagi 2): paghahati "para sa kita"

Idagdag ang partitioning key (petsa ng paksa) sa lahat ng mga talahanayan: paksa, kalahok.

Ngunit ngayon mayroon kaming dalawang problema nang sabay-sabay:

  • Sa aling seksyon ako dapat maghanap ng mga mensahe sa paksa?
  • Saang seksyon ko dapat hanapin ang paksa mula sa mensahe?

Maaari naming, siyempre, magpatuloy sa paghahanap sa lahat ng mga seksyon, ngunit ito ay magiging lubhang malungkot at magpapawalang-bisa sa lahat ng aming mga panalo. Samakatuwid, upang malaman kung saan eksaktong titingnan, gagawa kami ng mga lohikal na link/pointer sa mga seksyon:

  • idadagdag namin sa mensahe field ng petsa ng paksa
  • idagdag natin sa paksa itinakda ang petsa ng mensahe ang sulat na ito (maaaring isang hiwalay na talahanayan, o isang hanay ng mga petsa)

Messenger database (bahagi 2): paghahati "para sa kita"

Dahil magkakaroon ng kaunting pagbabago sa listahan ng mga petsa ng mensahe para sa bawat indibidwal na pagsusulatan (pagkatapos ng lahat, halos lahat ng mga mensahe ay nahuhulog sa 1-2 katabing araw), tututuon ko ang opsyong ito.

Sa kabuuan, ang istraktura ng aming database ay kinuha ang sumusunod na form, isinasaalang-alang ang partitioning:

Mga Talahanayan: RU, kung mayroon kang pag-ayaw sa Cyrillic alphabet sa mga pangalan ng mga talahanayan/patlang, mas mabuting huwag tumingin

-- сСкции ΠΏΠΎ Π΄Π°Ρ‚Π΅ сообщСния
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
);

Makatipid ng isang magandang sentimos

Well, paano kung hindi namin gamitin pagpipiliang klasikong sectioning batay sa pamamahagi ng mga value ng field (sa pamamagitan ng mga trigger at inheritance o PARTITION BY), at "manu-mano" sa antas ng aplikasyon, mapapansin mo na ang halaga ng partitioning key ay naka-imbak na sa pangalan ng talahanayan mismo.

Kaya kung ikaw ay gayon Masyado ka bang nag-aalala tungkol sa dami ng data na nakaimbak?, pagkatapos ay maaari mong alisin ang mga "dagdag" na field na ito at tugunan ang mga partikular na talahanayan. Totoo, ang lahat ng mga seleksyon mula sa ilang mga seksyon sa kasong ito ay kailangang ilipat sa panig ng aplikasyon.

Pinagmulan: www.habr.com

Magdagdag ng komento