Messenger-database (deel 2): ​​partitionering "voor winst"

We hebben met succes de structuur van onze PostgreSQL-database ontworpen voor het opslaan van correspondentie, een jaar is verstreken, gebruikers vullen deze actief in en nu miljoenen records, en ... iets begon alles te vertragen.

Messenger-database (deel 2): ​​partitionering "voor winst"
Feit is dat met de groei van het tafelvolume groeit ook de “diepte” van de indexen - zij het logaritmisch. Maar na verloop van tijd dwingt het de server om dezelfde lees-/schrijftaken uit te voeren vele malen meer gegevenspagina's verwerkendan in het begin.

Dit is waar het te hulp komt snijden.

Ik merk op dat het hier niet gaat om sharding, dat wil zeggen de distributie van gegevens tussen verschillende databases of servers. Omdat zelfs het delen van de gegevens door verscheidene servers, zult u het probleem van "zwellende" indexen in de loop van de tijd niet oplossen. Het is duidelijk dat als u het zich kunt veroorloven om elke dag een nieuwe server in gebruik te nemen, uw problemen niet langer op het vlak van een bepaalde database liggen.

We zullen geen specifieke scripts overwegen voor het implementeren van partitionering "in hardware", maar de aanpak zelf - wat en hoe "in plakjes gesneden" moet worden, en waar zo'n wens toe leidt.

concept

Nogmaals, we definiëren ons doel: we willen ervoor zorgen dat vandaag, morgen en een jaar later de hoeveelheid gegevens die door PostgreSQL wordt gelezen voor elke lees-/schrijfbewerking ongeveer hetzelfde blijft.

Voor enige chronologisch verzamelde gegevens (berichten, documenten, logs, archieven, ...) de natuurlijke keuze voor een partitiesleutel is datum/tijd van het evenement. In ons geval is dit evenement het moment dat het bericht is verzonden.

Merk op dat gebruikers bijna altijd werk alleen met de nieuwste dergelijke gegevens - ze lezen de laatste berichten, analyseren de laatste logs, ... Nee, natuurlijk kunnen ze verder terug in de tijd scrollen, alleen doen ze dat zeer zelden.

Uit deze beperkingen wordt duidelijk dat de optimale oplossing voor berichten zal zijn "dagelijkse" rubrieken - onze gebruiker leest tenslotte bijna altijd wat hem "vandaag" of "gisteren" is overkomen.

Als we gedurende de dag praktisch maar in één sectie schrijven en lezen, dan geeft dit ons ook efficiënter geheugen en schijfgebruik - aangezien alle sectie-indexen gemakkelijk in het RAM passen, in tegenstelling tot de "grote en dikke" in de hele tabel.

stap voor stap

Over het algemeen klinkt al het bovenstaande als één grote winst. En het is haalbaar, maar hiervoor zullen we hard moeten werken - omdat de beslissing om een ​​van de entiteiten te verdelen leidt tot de noodzaak om te "zagen" en te associëren.

Bericht, zijn eigenschappen en projecties

Aangezien we besloten hebben om berichten op datum in te delen, is het ook redelijk om de entiteitseigenschappen ervan afhankelijk te verdelen (bijgevoegde bestanden, lijst met ontvangers), en ook op postdatum.

Aangezien een van onze typische taken het bekijken van de berichtenregisters is (ongelezen, inkomend, alle), is het ook logisch om ze te "tekenen" in secties op berichtdatums.

Messenger-database (deel 2): ​​partitionering "voor winst"

We voegen de partitiesleutel (berichtdatum) toe aan alle tabellen: ontvangers, bestand, registers. U kunt het bericht zelf niet toevoegen, maar de bestaande DateTime gebruiken.

threads

Aangezien het onderwerp één is voor meerdere berichten, is het niet mogelijk om het in hetzelfde model te "knippen", het is noodzakelijk om op iets anders te vertrouwen. Perfect voor ons geval. datum van het eerste bericht in de correspondentie - dat wil zeggen, het moment van creatie, in feite, van het onderwerp.

Messenger-database (deel 2): ​​partitionering "voor winst"

Voeg een partitiesleutel (onderwerpdatum) toe aan alle tabellen: onderwerp, lid.

Maar nu hebben we twee problemen tegelijk:

  • in welke sectie moeten berichten over het onderwerp worden gezocht?
  • in welke sectie moet je zoeken naar het onderwerp van het bericht?

U kunt natuurlijk in alle secties blijven zoeken, maar het zal erg triest zijn en al onze winsten teniet doen. Daarom zullen we, om te weten waar we precies moeten zoeken, logische links / verwijzingen naar secties maken:

  • toevoegen aan het bericht onderwerp datum veld
  • toevoegen aan het onderwerp berichtdatum ingesteld deze correspondentie (u kunt een aparte tabel gebruiken, of u kunt een reeks datums gebruiken)

Messenger-database (deel 2): ​​partitionering "voor winst"

Aangezien er weinig wijzigingen zullen zijn in de lijst met berichtdata voor elke individuele correspondentie (bijna alle berichten vallen immers op 1-2 aangrenzende dagen), zal ik me concentreren op deze optie.

In totaal heeft de structuur van onze database de volgende vorm aangenomen, rekening houdend met partitionering:

Tabellen: 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
);

Bespaar een aardig centje

Nou, wat als we gebruiken klassiek snijden. gebaseerd op de distributie van veldwaarden (via triggers en overerving of PARTITION BY), maar "handmatig" op applicatieniveau, zult u merken dat de waarde van de partitiesleutel al is opgeslagen in de naam van de tabel zelf.

Dus als je dat bent maken zich grote zorgen over de hoeveelheid opgeslagen gegevens, dan kunt u deze "extra" velden verwijderen en specifiek adresseren voor specifieke tabellen. Het is waar dat alle selecties uit verschillende secties in dit geval al naar de applicatiezijde moeten worden overgebracht.

Bron: www.habr.com

Voeg een reactie