Design do banco de dados - Armazenar estado ou estado de computação todas as vezes?

17

Digamos que eu tenha um aplicativo de banco de dados relacional e um objeto "usuário" e um objeto "mensagem". Agora, quero mostrar o número de mensagens não lidas para esse usuário.

Qual é a melhor maneira de arquivar isso? Eu introduzo um campo no usuário e o contabilizo se o usuário receber uma mensagem e diminuo a contagem se ele ler uma? Ou executo uma consulta sempre para calcular o número de mensagens para o usuário sinalizadas como não lidas?

Acho que a primeira abordagem é mais complicada e propensa a erros, mas terá um desempenho melhor que a segunda.

Como isso é feito normalmente ou qual é a melhor abordagem?

jan
fonte
1
Depende de vários fatores: seu banco de dados está particionado? Quantas linhas / usuário você espera? Qual o tamanho total do banco de dados que você espera (ou quantos usuários no total)? Quantas solicitações por segundo você espera? Tudo isso não tem que ser preciso, mas algumas idéias ásperas ...
Omer Iqbal
10
+1 Esta é uma pergunta clássica de banco de dados relacional. Normalizar ou não normalizar? Essa é a questão. Se no esquema é mais nobre sofrer as arremessos e flechas da duplicação ultrajante, ou pegar gatilhos e, ao empregá-los, acabar com eles?
Ross Patterson
Eu argumento se isso é um rel clássico. db. pergunta, já deve haver uma resposta no site, isso deve ser fechado como DUP, ou não temos uma resposta e isso deve ser deixado em aberto.
mattnz

Respostas:

14

Como isso é feito normalmente ou qual é a melhor abordagem?

A melhor abordagem é tentar primeiro sem um campo extra, medir o desempenho e, se realmente for muito lento, tente otimizar. Isso pode significar mudar para sua primeira abordagem usando um campo extra, mas considere testar outras opções, por exemplo, colocando um índice extra nos campos combinados ("não lidos", "ID do usuário") em suas mensagens.

Doc Brown
fonte
2
A melhor abordagem é (siga primeiro o método mais simples). Regras gerais são melhores que específicas, fwiw. (+1 para "teste!").
DougM 24/06
9

A solução do livro didático de acordo com a teoria do banco de dados seria não ter valores no banco de dados que dependam dos valores de outros dados, porque são dependências transitivas . Ter campos que são valores calculados com base em outros campos é uma violação da normalização, pois leva a informações redundantes.

No entanto, às vezes o que o livro diz e qual é o método mais prático na prática são diferentes. Contar o número de mensagens não lidas em cada visualização de página pode ser uma operação bastante cara. Armazenar em cache o número na usertabela-seria muito melhor para o desempenho. O custo seria a possibilidade de existir inconsistências no banco de dados: pode ser possível que uma mensagem seja excluída, adicionada ou lida sem se lembrar de também atualizar o contador não lido.

Philipp
fonte
4
É fácil lamber o problema de consistência com gatilhos que ajustam o contador INSERTou DELETE. (Ou UPDATE, para explicar a alteração do proprietário de uma mensagem.). Um bom DBMS fará a operação e executará os gatilhos na mesma transação, para que tudo ou nada aconteça.
Blrfl
4

O problema potencial é o desempenho e você ainda não tem um problema de desempenho. Há muitas coisas que você pode fazer, dependendo do banco de dados de escolha, para lidar com isso na solução nº 1: indexação, hardware, cache etc. Isso tudo depende da frequência com que o usuário precisa obter uma contagem atual de mensagens não lidas. Muitas dessas opções não exigem codificação personalizada no lado do aplicativo, para que você possa implementá-las com uma alteração de código ou muito pouco. Facilita o crescimento com o aplicativo.

Depois que um usuário se conecta / efetua login, obter a contagem do banco de dados uma vez não é tão ruim. Seu aplicativo manterá uma lista constantemente atualizada de mensagens como e-mail? Obter uma contagem não lida a partir daqui não requer outra viagem ao banco de dados e receber novas mensagens fará uma viagem de banco de dados de qualquer maneira.

Viajando para o banco de dados toda vez que uma mensagem é lida para sinalizar o IsRead? campo é suficiente sem um recálculo de outro campo.

Com a solução 2 (mantendo uma contagem em um campo / em disco), você precisará de uma rotina para reconstruir / recalcular periodicamente esse campo quando houver um problema? E sempre há problemas. Você vai agrupar tudo isso em uma transação? Sempre que alguém envia uma mensagem a outra pessoa, ela pode falhar porque não pode atualizar o UnreadCount do usuário receptor devido a um bloqueio da tabela Usuário? Ou você criará uma tabela separada para esse campo?

JeffO
fonte
+1 por mencionar os problemas de desempenho em manter os campos de contagem atualizados
winkbrace 27/06
0

O jeito que eu faria isso é executando uma consulta sempre, ou seja, sua segunda abordagem. Apenas certifique-se de adicionar um índice em sua tabela de mensagens na coluna que atua como uma chave estrangeira na tabela de usuários para melhorar o desempenho da sua consulta.

Como o Doc diz, meça o desempenho dessa abordagem e você poderá saber se precisa seguir um caminho diferente.

Jose B
fonte