Progettazione del database - Memorizza lo stato o lo stato di calcolo ogni volta?


17

Diciamo che ho un'applicazione di database relazionale e un oggetto "utente" e un oggetto "messaggio". Ora voglio mostrare il numero di messaggi non letti a questo utente.

Qual è il modo migliore per archiviarlo? Introduco un campo nell'utente e lo conto se l'utente riceve un messaggio e diminuisco il conteggio se ne legge uno? Oppure eseguo una query ogni volta per calcolare il numero di messaggi per l'utente che sono contrassegnati come non letti?

Penso che il primo approccio sia più complicato e soggetto a errori, ma funzionerà meglio del secondo approccio.

Come si fa normalmente o qual è l'approccio migliore?


1
Dipende da una serie di fattori: il tuo DB è partizionato? Quante righe / utente ti aspetti? Che dimensione totale ti aspetti (o quanti utenti totali)? Quante richieste al secondo ti aspetti? Tutto ciò non deve essere accurato, ma alcune idee approssimative ...
Omer Iqbal,

10
+1 Questa è una classica domanda sul database relazionale. Per normalizzare o non normalizzare? Questa è la domanda. Se non è più nobile nello schema subire le imbragature e le frecce della duplicazione oltraggiosa, o prendere inneschi, e impiegandoli, finirli?
Ross Patterson,

Discuto se questo è indead un classico Rel. db. domanda, ci dovrebbe già essere una risposta sul sito, questo dovrebbe essere chiuso come DUP, o non abbiamo una risposta e questo dovrebbe essere lasciato aperto.
Mattnz,

Risposte:


14

Come si fa normalmente o qual è l'approccio migliore?

L'approccio migliore è provarlo prima senza un campo aggiuntivo, misurare le prestazioni e, se risulta davvero troppo lento, si tenta di ottimizzare. Ciò potrebbe significare passare al tuo primo approccio utilizzando un campo aggiuntivo, ma dovresti considerare di provare anche altre opzioni, ad esempio inserendo un indice aggiuntivo nei campi combinati ("non letto", "ID utente") sui tuoi messaggi.


2
L'approccio migliore è quello di (andare prima con il metodo più semplice). Le regole generali sono migliori delle specifiche, prima. (+1 per "test!").
DougM,

9

La soluzione da manuale secondo la teoria del database sarebbe quella di non avere valori nel database che dipendono dai valori di altri dati, poiché si tratta di dipendenze transitive . Avere campi che sono valori calcolati sulla base di altri campi è una violazione della normalizzazione, perché porta a informazioni ridondanti.

Tuttavia, a volte ciò che dice il libro di testo e qual è il metodo più pratico in pratica differisce. Contare il numero di messaggi non letti ogni visualizzazione di pagina potrebbe essere un'operazione piuttosto costosa. La memorizzazione nella cache del numero nella usertabella sarebbe molto migliore per le prestazioni. Il costo sarebbe che potrebbero esistere incoerenze nel database: potrebbe essere possibile che un messaggio venga cancellato, aggiunto o letto senza ricordare di aggiornare anche il contatore non letto.


4
Il problema della coerenza è facile da leccare con i trigger che regolano il contatore su INSERTo DELETE. (Oppure UPDATE, per tenere conto della modifica del proprietario di un messaggio.). Un buon DBMS eseguirà l'operazione ed eseguirà i trigger nella stessa transazione, quindi accadrà tutto o nessuno.
Blrfl,

4

Il potenziale problema è la prestazione e non hai ancora un problema di prestazione. Ci sono molte cose che puoi fare a seconda del database scelto per gestirlo nella soluzione n. 1: indicizzazione, hardware, memorizzazione nella cache, ecc. Tutto dipende dalla frequenza con cui l'utente deve ottenere un conteggio dei messaggi non ancora letto. Molte di queste scelte non richiedono una codifica personalizzata sul lato app, quindi puoi implementarle con una modifica del codice o molto poco. Semplifica la crescita con l'app.

Una volta che un utente si collega / accede, ottenere il conteggio dal database una volta non è poi così male. La tua app manterrà un elenco di messaggi in costante aggiornamento come la posta elettronica? Ottenere un conteggio non letto da qui non richiede un altro viaggio nel database e per ricevere nuovi messaggi farà comunque un viaggio db.

Fare un viaggio nel db ogni volta che viene letto un messaggio per contrassegnare IsRead? campo è sufficiente senza un ricalcolo di un altro campo.

Con la soluzione n. 2 (mantenere un conteggio in un campo / su disco), sarà necessaria una routine per ricostruire / ricalcolare periodicamente questo campo quando si verifica un problema? E ci sono sempre problemi. Avvolgerai tutto questo in una transazione? Ogni volta che qualcuno invia a qualcun altro un messaggio, potrebbe non riuscire perché non può aggiornare UnreadCount dell'utente ricevente a causa di un blocco della tabella Utente? O hai intenzione di creare una tabella separata per questo campo?


+1 per menzionare i problemi di prestazioni con l'aggiornamento dei campi di conteggio
winkbrace,

0

Il modo in cui lo farei è eseguendo una query ogni volta, ovvero il tuo secondo approccio. Assicurati solo di aggiungere un indice nella tabella dei messaggi nella colonna che funga da chiave esterna per la tabella degli utenti per migliorare le prestazioni della tua query.

Quindi, come dice Doc, misura le prestazioni di questo approccio e sarai in grado di dire se devi prendere una strada diversa.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.