Banco de dados do Messenger (parte 1): projetamos o quadro base

Como os requisitos de negócios podem ser traduzidos em estruturas de dados específicas usando o exemplo de design de uma base "do zero" para um messenger?



Nosso banco de dados não será tão grande e distribuído como o do VKontakte ou Badoo , mas "estar", mas é bom - funcional, rápido e se encaixa em um único servidor PostgreSQL - para que você possa implantar uma instância separada do serviço em algum lugar do lado, por exemplo.

Portanto, não abordaremos os problemas de sharding, replicação e sistemas de distribuição geográfica, mas focaremos nas soluções de circuitos dentro do banco de dados.

Etapa 1: um pouco de especificidade comercial


Não projetaremos nossas mensagens em abstrato, mas as incorporaremos no ambiente de rede social corporativa . Ou seja, as pessoas aqui não estão apenas “enviando mensagens de texto”, mas se comunicando entre si no contexto de resolver certos problemas de negócios.

E quais são os desafios de negócios? ... Vejamos o exemplo de Vasily, chefe do departamento de desenvolvimento.
  • "Nikolai, esse patch precisa desse patch hoje!"
    Portanto, a correspondência pode ser conduzida no contexto de um documento .
  • "Kolya, vá para DotA à noite?"
    Ou seja, mesmo com um par de interlocutores, a comunicação ao mesmo tempo pode ser conduzida em diferentes tópicos .
  • "Peter, Nikolai, veja o anexo de preços no novo servidor."
    Portanto, uma mensagem pode ter vários destinatários . Nesse caso, a mensagem pode conter arquivos anexados .
  • "Semyon, e você também está."
    E deve ser possível convidar um novo membro para uma correspondência existente.

Vamos nos debruçar sobre esta lista de necessidades "óbvias".
Sem uma compreensão das especificidades aplicadas da tarefa e das restrições definidas por ela, é praticamente impossível projetar um esquema de banco de dados eficaz para resolvê-la.

Etapa 2: Lógica Mínima


Até agora, tudo se parece muito com a correspondência por email - uma ferramenta comercial tradicional. Portanto, sim, “algoritmicamente”, muitas tarefas de negócios são semelhantes entre si e, portanto, as ferramentas para resolvê-las serão estruturalmente semelhantes.

Vamos corrigir o diagrama lógico resultante das relações das entidades. Para simplificar a compreensão de nosso modelo, usaremos a opção mais primitiva para exibir um modelo de ER sem as complicações das notações UML ou IDEF:



Em nosso exemplo, a pessoa, o documento e o "corpo" binário do arquivo são entidades "externas" que existem independentemente sem o nosso serviço. Portanto, nós simplesmente os perceberemos no futuro como alguns links "em algum lugar" do UUID.
Desenhe os diagramas da maneira mais simples possível - a maioria daqueles a quem você os mostrará não são especialistas em ler UML / IDEF. Mas - não se esqueça de desenhar.

Etapa 3: esboçar a estrutura da tabela


Sobre nomes de tabela e campo
Os nomes "russos" de campos e tabelas podem ser tratados de maneira diferente, mas isso é uma questão de gosto. Como não temos desenvolvedores estrangeiros no “Tensor” , o PostgreSQL nos permite dar nomes mesmo com hieróglifos, se eles estiverem entre aspas , preferimos nomear objetos de forma clara e sem ambiguidade, para que não haja mal-entendidos.

Como muitas pessoas escrevem mensagens ao mesmo tempo, algumas delas podem fazer isso offline , a opção mais fácil é usar UUIDs como identificadores, não apenas para entidades externas, mas para todos os objetos dentro de nosso serviço. Além disso, eles podem ser gerados mesmo no lado do cliente - isso nos ajudará a suportar o envio de mensagens com inacessibilidade a curto prazo do banco de dados, e a probabilidade de uma colisão é extremamente pequena.

A estrutura aproximada das tabelas em nosso banco de dados terá a seguinte aparência:
Mesas: RU
CREATE TABLE ""( "" uuid PRIMARY KEY , "" uuid , "" text ); CREATE TABLE ""( "" uuid PRIMARY KEY , "" uuid , "" uuid , "" timestamp , "" text ); CREATE TABLE ""( "" uuid , "" uuid , PRIMARY KEY("", "") ); CREATE TABLE ""( "" uuid PRIMARY KEY , "" uuid , "BLOB" uuid , "" text ); 

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

A maneira mais fácil de descrever o formato é começar a "desvendar" o gráfico dos links das tabelas que não se referem a ninguém.

Etapa 4: Descobrindo requisitos não óbvios


É isso, criamos uma base na qual você pode escrever e ler de alguma forma .

Vamos nos colocar no lugar do usuário do nosso serviço - o que queremos fazer com ele?

  • Posts recentes
    Este é um registro de "minhas" mensagens ordenadas cronologicamente por várias características. Onde eu sou um dos destinatários, onde eu sou o autor, onde eles me escreveram, mas eu não respondi, onde eles não me responderam ...
  • Correspondentes
    Quem está envolvido nesse longo e longo bate-papo?

Nossa estrutura nos permite resolver esses dois problemas "em geral", mas rapidamente - não. O problema é que, para classificar como parte da primeira tarefa, é impossível criar um índice adequado para cada um dos participantes (e você precisará recuperar todos os registros) e, para resolver o segundo, é necessário recuperar todas as mensagens sobre o tópico.
Tarefas imprevistas do usuário podem acabar com a produtividade .

Etapa 5: Desnormalização razoável


Ambos os nossos problemas ajudarão a resolver tabelas adicionais, nas quais duplicaremos parte dos dados necessários para formar índices adequados para nossos problemas neles.


Mesas: RU
 CREATE TABLE ""( "" uuid , "" smallint , "" timestamp , "" uuid , PRIMARY KEY("", "", "") ); CREATE INDEX ON ""("", "", "" DESC); CREATE TABLE ""( "" uuid , "" uuid , PRIMARY KEY("", "") ); 

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

Aqui nós aplicamos duas abordagens típicas usadas para criar tabelas auxiliares:

  • Registros de multiplicação
    Formamos vários registros de origem de uma vez a partir de um registro de origem da mensagem em diferentes tipos de registros para proprietários diferentes - tanto para o remetente quanto para o destinatário. Mas cada um dos registros agora está no índice - porque, no caso típico, queremos ver apenas a primeira página.
  • Exclusão de registro
    Cada vez que você envia uma mensagem em um tópico específico, basta verificar se essa entrada já existe. Caso contrário, adicione-o ao nosso "dicionário".

Na próxima parte do artigo, discutiremos a implementação de seções na estrutura de nosso banco de dados.

Source: https://habr.com/ru/post/pt483176/


All Articles