Database di Messenger (parte 2): partizionamento “a scopo di lucro”

Abbiamo progettato con successo la struttura del nostro database PostgreSQL per l'archiviazione della corrispondenza, è passato un anno, gli utenti lo stanno riempiendo attivamente e ora contiene milioni di record, e... qualcosa ha iniziato a rallentare.

Database di Messenger (parte 2): partizionamento “a scopo di lucro”
Il fatto è che Man mano che cresce la dimensione della tabella, aumenta anche la “profondità” degli indici. - anche se logaritmicamente. Ma nel tempo ciò costringe il server a eseguire le stesse attività di lettura/scrittura elaborare molte più pagine di datiche all'inizio.

Ecco dove viene in soccorso sezionando.

Faccio notare che non stiamo parlando di sharding, ovvero di distribuzione dei dati tra diversi database o server. Perché anche dividendo i dati in un po 'di server, non ti libererai del problema del “gonfiore” degli indici nel tempo. È chiaro che se puoi permetterti di mettere in funzione un nuovo server ogni giorno, i tuoi problemi non risiedono più nel piano di un database specifico.

Considereremo non script specifici per implementare il partizionamento "nell'hardware", ma l'approccio stesso: cosa e come dovrebbe essere "tagliato a fette" e a cosa porta tale desiderio.

concetto

Definiamo ancora una volta il nostro obiettivo: vogliamo assicurarci che oggi, domani e tra un anno, la quantità di dati letti da PostgreSQL durante qualsiasi operazione di lettura/scrittura rimanga approssimativamente la stessa.

Per ogni dati accumulati cronologicamente (messaggi, documenti, registri, archivi, ...) è la scelta naturale come chiave di partizionamento data/ora dell'evento. Nel nostro caso, un evento del genere lo è momento dell'invio del messaggio.

Tieni presente che gli utenti quasi sempre funzionano solo con quelli “più recenti”. tali dati: leggono gli ultimi messaggi, analizzano gli ultimi log,... No, ovviamente possono scorrere più indietro nel tempo, ma lo fanno molto raramente.

Da questi vincoli è chiaro che la soluzione di messaggio ottimale sarebbe sezioni "quotidiane". - dopotutto, il nostro utente leggerà quasi sempre ciò che gli è venuto in mente “oggi” o “ieri”.

Se scriviamo e leggiamo quasi solo in una sezione durante il giorno, anche questo ci dà utilizzo più efficiente della memoria e del disco - poiché tutti gli indici di sezione si adattano facilmente alla RAM, a differenza di quelli "grandi e grossi" in tutta la tabella.

passo dopo passo

In generale, tutto ciò che è stato detto sopra sembra un profitto continuo. Ed è realizzabile, ma per questo dovremo impegnarci al massimo, perché la decisione di frazionare una delle entità comporta la necessità di “segare” gli associati.

Il messaggio, sue proprietà e proiezioni

Poiché abbiamo deciso di suddividere i messaggi per data, è opportuno dividere anche le entità-proprietà che dipendono da essi (file allegati, lista dei destinatari), e anche per data del messaggio.

Poiché uno dei nostri compiti tipici è proprio quello di visualizzare i registri dei messaggi (non letti, in arrivo, tutti), è anche logico “inserirli” in una suddivisione per data dei messaggi.

Database di Messenger (parte 2): partizionamento “a scopo di lucro”

Aggiungiamo la chiave di partizionamento (data del messaggio) a tutte le tabelle: destinatari, file, registri. Non è necessario aggiungerlo al messaggio stesso, ma utilizzare il DateTime esistente.

discussioni

Dato che esiste un solo argomento per più messaggi, non c’è modo di “tagliarlo” nello stesso modello; devi fare affidamento su qualcos’altro. Nel nostro caso è l'ideale data del primo messaggio nella corrispondenza – cioè il momento della creazione, appunto, dell’argomento.

Database di Messenger (parte 2): partizionamento “a scopo di lucro”

Aggiungi la chiave di partizionamento (data dell'argomento) a tutte le tabelle: argomento, partecipante.

Ma ora abbiamo due problemi contemporaneamente:

  • In quale sezione devo cercare i messaggi sull'argomento?
  • In quale sezione devo cercare l'argomento del messaggio?

Naturalmente possiamo continuare a cercare in tutte le sezioni, ma questo sarà molto triste e annullerà tutte le nostre vincite. Pertanto, per sapere esattamente dove cercare, creeremo collegamenti/puntamenti logici alle sezioni:

  • aggiungeremo nel messaggio campo data dell'argomento
  • aggiungiamo all'argomento data del messaggio impostata questa corrispondenza (può essere una tabella separata o un array di date)

Database di Messenger (parte 2): partizionamento “a scopo di lucro”

Poiché ci saranno poche modifiche all'elenco delle date dei messaggi per ogni singola corrispondenza (dopo tutto, quasi tutti i messaggi cadono in 1-2 giorni adiacenti), mi concentrerò su questa opzione.

In totale, la struttura del nostro database ha assunto la seguente forma, tenendo conto della partizione:

Tabelle: RU, se hai un'avversione per l'alfabeto cirillico nei nomi delle tabelle/campi, è meglio non cercare

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

Risparmia un bel soldo

Bene, e se non lo usassimo? opzione di sezionamento classico in base alla distribuzione dei valori dei campi (tramite trigger ed ereditarietà o PARTITION BY), e “manualmente” a livello applicativo, noterai che il valore della chiave di partizionamento è già memorizzato nel nome della tabella stessa.

Quindi se sei così Sei molto preoccupato per la quantità di dati archiviati?, quindi puoi eliminare questi campi "extra" e indirizzare tabelle specifiche. È vero, tutte le selezioni da più sezioni in questo caso dovranno essere trasferite sul lato dell'applicazione.

Fonte: habr.com

Aggiungi un commento