DB del messenger (parte 1): progettazione della struttura del database

Come tradurre i requisiti aziendali in strutture dati specifiche utilizzando come esempio la progettazione di un database per un messenger "da zero".

DB del messenger (parte 1): progettazione della struttura del database
Il nostro database non sarà così grande e distribuito, come quello di VKontakte o Badoo, ma "per averlo", deve essere fatto bene: funzionale, veloce e stare su un unico server. PostgreSQL — in modo da poter disporre di un'istanza separata del servizio da qualche parte, per esempio.

Pertanto, non toccheremo le questioni di sharding, replica e sistemi geo-distribuiti, ma ci concentreremo sulle soluzioni schemiche all'interno del database.

Passo 1: Un po' di specificità aziendale.

La nostra messaggistica sarà progettata non in modo astratto, ma integrata nell'ambiente di un social network aziendale.Cioè, le persone non "si scrivono solo", ma comunicano tra di loro nel contesto della risoluzione di specifici compiti aziendali.

E quali compiti ha un'azienda?.. Vediamo l'esempio di Vasiliy, il capo del reparto sviluppo.

  • "Nikolai, per questo compito è necessaria una patch già oggi!"
    Quindi, la corrispondenza può avvenire nel contesto di qualche documento.
  • "Kolia, andiamo stasera a giocare a Dota?"
    Cioè, anche tra una coppia di interlocutori, la comunicazione può avvenire simultaneamente su diversi temi..
  • "Petr, Nikolai, guarda in allegato il prezzo per il nuovo server."
    Quindi, un messaggio può avere diversi destinatari.Inoltre, un messaggio può contenere file allegati..
  • "Semyon, dai un'occhiata anche tu."
    E ci deve essere la possibilità di invitare un nuovo partecipante in una corrispondenza già esistente..

Fermiamoci per ora su questo elenco di esigenze "ovvie".

Senza comprendere la specificità applicativa del compito e le limitazioni imposte, progettare uno schema di database efficace per la sua soluzione è praticamente impossibile.

Passo 2: Schema logico minimo.

Finora lo schema appare molto simile a una corrispondenza email: uno strumento tradizionale per il business. Infatti, "algoritmicamente" molte esigenze aziendali si somigliano, quindi anche gli strumenti per la loro soluzione saranno strutturalmente simili.

Fissiamo ora lo schema logico delle relazioni tra le entità già ottenuto. Per facilità di comprensione del nostro modello, utilizzeremo la variante più primitiva di rappresentazione del modello ER senza complicazioni di notazioni UML o IDEF:

DB del messenger (parte 1): progettazione della struttura del database

Nel nostro esempio, la persona, il documento e il «corpo» binario del file sono entità «esterne» che esistono autonomamente anche senza il nostro servizio. Quindi, considereremo in seguito queste entità come dei riferimenti «da qualche parte» tramite UUID.

Disegnate schemi il più semplici possibile — la maggior parte di quelli a cui li mostrerete non sono esperti nella lettura di UML/IDEF. Ma - disegnate comunque.

Passo 3: Bozza della struttura delle tabelle

Sugli nomi delle tabelle e dei campiI nomi "russi" dei campi e delle tabelle possono essere considerati in vari modi, ma è una questione di gusto. Dato che non abbiamo sviluppatori stranieri in "Tensor" e PostgreSQL ci consente di dare nomi anche con i geroglifici, se sono racchiusi tra virgolette, preferiamo nominare gli oggetti in modo chiaro e ovvio, per evitare ambiguità.
Poiché ci sono molte persone che scrivono messaggi, alcuni di loro potrebbero farlo offline, la soluzione più semplice è utilizzare UUID come identificatori non solo per entità esterne, ma anche per tutti gli oggetti all'interno del nostro servizio. Inoltre, possono essere generati anche dal lato client - questo ci aiuterà a supportare l'invio dei messaggi in caso di temporanea indisponibilità del DB, e la probabilità di collisione è estremamente bassa.

La struttura di base delle tabelle nel nostro database avrà questo aspetto:
Tabelle : RU

CREATE TABLE "Tema"(
  "Tema"
    uuid
      PRIMARY KEY
, "Documento"
    uuid
, "Titolo"
    text
);

CREATE TABLE "Messaggio"(
  "Messaggio"
    uuid
      PRIMARY KEY
, "Tema"
    uuid
, "Autore"
    uuid
, "DataOra"
    timestamp
, "Testo"
    text
);

CREATE TABLE "Destinatario"(
  "Messaggio"
    uuid
, "Persona"
    uuid
, PRIMARY KEY("Messaggio", "Persona")
);

CREATE TABLE "File"(
  "File"
    uuid
      PRIMARY KEY
, "Messaggio"
    uuid
, "BLOB"
    uuid
, "Nome"
    text
);

Tabelle : EN

CREATE TABLE theme(
  theme
    uuid
      PRIMARY KEY
, document
    uuid
, title
    text
);

CREATE TABLE message(
  message
    uuid
      PRIMARY KEY
, theme
    uuid
, author
    uuid
, dt
    timestamp
, body
    text
);

CREATE TABLE message_addressee(
  message
    uuid
, person
    uuid
, PRIMARY KEY(message, person)
);

CREATE TABLE message_file(
  file
    uuid
      PRIMARY KEY
, message
    uuid
, content
    uuid
, filename
    text
);

La cosa più semplice quando si descrive il formato è iniziare a «sviluppare» il grafo di relazioni dalle tabelle che non si riferiscono a nessuna.

Passo 4: Scoprire le esigenze non ovvie

Bene, abbiamo progettato un database in cui possiamo ottimamente scrivere e in qualche modo leggere.

Mettiamoci nei panni dell'utente del nostro servizio: cosa vorremmo fare con il suo aiuto?

  • Ultimi messaggi
    Questo ordinati cronologicamente un registro «dei miei» messaggi per vari criteri. Dove io sono uno dei destinatari, dove sono l'autore, dove mi hanno scritto e non ho risposto, dove non mi hanno risposto, …
  • Partecipanti alla conversazione
    Chi partecipa a questa lunga conversazione?

La nostra struttura consente di risolvere entrambi questi problemi «in generale», ma velocemente — no. Il problema è che per ordinare nell'ambito del primo problema è impossibile creare un indice, adatto per ciascun partecipante (e sarà necessario estrarre tutte le registrazioni), e per risolvere il secondo è necessario estrarre tutti i messaggi relative all'argomento.

Compiti utente non previsti possono compromettere seriamente le prestazioni.

Passo 5: Denormalizzazione sensata

Entrambi i nostri problemi possono essere risolti da tabelle aggiuntive, in cui possiamo duplicare parte dei dati, necessari per formare indici adeguati ai nostri compiti.
DB del messenger (parte 1): progettazione della struttura del database

Tabelle : RU

CREATE TABLE "RegistroMessaggi"(
  "Proprietario"
    uuid
, "TipoRegistro"
    smallint
, "DataOra"
    timestamp
, "Messaggio"
    uuid
, PRIMARY KEY("Proprietario", "TipoRegistro", "Messaggio")
);
CREATE INDEX ON "RegistroMessaggi"("Proprietario", "TipoRegistro", "DataOra" DESC);

CREATE TABLE "PartecipanteTema"(
  "Tema"
    uuid
, "Persona"
    uuid
, PRIMARY KEY("Tema", "Persona")
);

Tabelle : EN

CREATE TABLE message_registry(
  owner
    uuid
, registry
    smallint
, dt
    timestamp
, message
    uuid
, PRIMARY KEY(owner, registry, message)
);
CREATE INDEX ON message_registry(owner, registry, dt DESC);

CREATE TABLE theme_participant(
  theme
    uuid
, person
    uuid
, PRIMARY KEY(theme, person)
);

Qui abbiamo applicato due approcci tipici utilizzati per la creazione di tabelle ausiliarie:

  • Moltiplicazione dei record
    Creiamo da una singola registrazione originale del messaggio diverse registrazioni correlate in diversi tipi di registro per diversi proprietari — sia per il mittente che per il destinatario. Ora ogni registro può essere posizionato su un indice — infatti, nel caso tipico vorremmo vedere solo la prima pagina.
  • Unicizzazione dei record
    Ad ogni invio di messaggio all'interno di un tema specifico è sufficiente controllare se esiste già una registrazione di quel tipo. Se no — la aggiungiamo al nostro «dizionario».

Nella prossima parte dell'articolo parleremo di implementazione della partizione nella struttura del nostro database.

Fonte: habr.com

Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server 🔥 Acquista hosting affidabile per siti web con protezione DDoS, VPS VDS server | ProHoster