Quale è più efficiente: più tabelle MySQL o una grande tabella?


103

Memorizzo vari dettagli utente nel mio database MySQL. Originariamente era impostato in varie tabelle, il che significa che i dati sono collegati con UserIds e l'output tramite chiamate a volte complicate per visualizzare e manipolare i dati come richiesto. Impostando un nuovo sistema, ha quasi senso combinare tutte queste tabelle in un'unica grande tabella di contenuti correlati.

  • Questo sarà un aiuto o un ostacolo?
  • Considerazioni sulla velocità nella chiamata, nell'aggiornamento o nella ricerca / manipolazione?

Ecco un esempio di alcune delle mie strutture di tabella:

  • utenti: ID utente, nome utente, e-mail, password crittografata, data di registrazione, ip
  • user_details - dati dei cookie, nome, indirizzo, dettagli di contatto, affiliazione, dati demografici
  • user_activity - contributi, ultimo accesso online, ultima visualizzazione
  • user_settings - impostazioni di visualizzazione del profilo
  • user_interests - variabili pubblicizzabili come target
  • user_levels - diritti di accesso
  • user_stats - hit, conteggi

Modifica: ho votato tutte le risposte finora, tutte hanno elementi che essenzialmente rispondono alla mia domanda.

La maggior parte delle tabelle ha una relazione 1: 1 che è stata la ragione principale per denormalizzarle.

Ci saranno problemi se la tabella si estende su oltre 100 colonne quando è probabile che una gran parte di queste celle rimanga vuota?


Questa altra domanda potrebbe essere utile anche
Mosty Mostacho

Risposte:


65

Più tabelle aiutano nei seguenti modi / casi:

(a) se persone diverse svilupperanno applicazioni che coinvolgono tabelle diverse, ha senso dividerle.

(b) Se si desidera assegnare diversi tipi di autorità a persone diverse per parti diverse della raccolta dei dati, potrebbe essere più conveniente dividerle. (Ovviamente, puoi esaminare la definizione delle viste e dare loro l'autorizzazione in modo appropriato).

(c) Per spostare i dati in luoghi diversi, specialmente durante lo sviluppo, può avere senso utilizzare tabelle che producono file di dimensioni inferiori.

(d) Un footprint più piccolo può dare conforto mentre si sviluppano applicazioni sulla raccolta di dati specifici di una singola entità.

(e) È una possibilità: ciò che pensavi come un singolo valore di dati potrebbe rivelarsi davvero più valori in futuro. ad esempio, il limite di credito è un campo di valore singolo al momento. Ma domani, potresti decidere di modificare i valori come (data da, data a, valore del credito). Le tabelle divise potrebbero tornare utili ora.

Il mio voto sarebbe per più tabelle, con i dati opportunamente suddivisi.

In bocca al lupo.


3
@RohitKhatri: Per quanto ne so, avere più tabelle aumenterà le prestazioni nella maggior parte dei casi.
Hari Harker

1
@HariHarker Grazie per la tua risposta, ma ho capito che dipende dal tuo schema di accesso.
Rohit Khatri

Fino a poco tempo fa memorizzavo sempre tutti i dati in una tabella, ma a pensarci bene, ha molti vantaggi dividere i dati in termini di prestazioni (a seconda del caso d'uso, naturalmente), semantica (alcuni dati sono raggruppati meglio in un tabella diversa) e sviluppo. Ad esempio, sto sviluppando un sistema ERP personalizzato proprio ora sopra un sistema legacy. Ho dovuto espandere le vecchie tabelle del database con colonne extra. Ho deciso di creare nuove tabelle per i nuovi dati. Alcune nuove funzionalità tornano utili per il sistema precedente e ora posso integrarle facilmente senza dover riscrivere troppo le vecchie query
Ogier Schelvis

35

La combinazione delle tabelle si chiama denormalizzazione.

Può (o non può) aiutare a fare alcune query (che fanno molti JOIN) per essere eseguite più velocemente a scapito di creare un inferno di manutenzione.

MySQLè in grado di utilizzare solo il JOINmetodo, vale a dire NESTED LOOPS.

Ciò significa che per ogni record nella tabella MySQLpilotata , individua un record corrispondente nella tabella pilotata in un ciclo.

L'individuazione di un record è un'operazione piuttosto costosa che può richiedere decine di volte il tempo della scansione del record puro.

Spostare tutti i record in una tabella ti aiuterà a sbarazzarti di questa operazione, ma la tabella stessa diventa più grande e la scansione della tabella richiede più tempo.

Se si dispone di molti record in altre tabelle, l'aumento della scansione della tabella può sovraccaricare i vantaggi dei record sottoposti a scansione sequenziale.

L'inferno della manutenzione, invece, è garantito.


1
Se hai 10000 utenti e stai facendo un join con un database configurato correttamente con chiavi esterne, allora dovresti solo aver bisogno di una ricerca intensa facendo qualcosa come select * from users where name = "bob". Una volta che hai bob, stai usando un indice per trovare le tabelle unite a bob, che è significativamente più veloce perché stai usando l'id di bob. Ciò accade indipendentemente dal fatto che tu stia facendo un join nella tua query o interrogando bob, quindi interrogando una tabella separatamente. Ovviamente si spera che la tua seconda query sia basata sull'ID di Bob e non su qualcos'altro.
Rudy Garcia

17

Sono tutte relazioni 1: 1? Voglio dire, se un utente potesse appartenere, ad esempio, a diversi livelli utente, o se gli interessi degli utenti fossero rappresentati come diversi record nella tabella degli interessi degli utenti, l'unione di quelle tabelle sarebbe immediatamente fuori questione.

Per quanto riguarda le risposte precedenti sulla normalizzazione, va detto che le regole di normalizzazione del database hanno completamente ignorato le prestazioni e stanno solo esaminando ciò che è un design accurato del database. Questo è spesso ciò che vuoi ottenere, ma ci sono momenti in cui ha senso denormalizzarsi attivamente alla ricerca della performance.

Tutto sommato, direi che la domanda si riduce a quanti campi ci sono nelle tabelle e quanto spesso vi si accede. Se l'attività dell'utente spesso non è molto interessante, potrebbe essere solo un fastidio averla sempre sullo stesso record, per motivi di prestazioni e manutenzione. Se si accede ad alcuni dati, ad esempio le impostazioni, molto spesso, ma contengono semplicemente troppi campi, potrebbe non essere conveniente unire le tabelle. Se sei interessato solo al miglioramento delle prestazioni, potresti prendere in considerazione altri approcci, come mantenere separate le impostazioni, ma salvarle in una variabile di sessione propria in modo da non dover interrogare il database per loro molto spesso.


Devo essere completamente in disaccordo con il tuo commento secondo cui la normalizzazione si concentra solo sulla pulizia e ignora completamente le prestazioni. C'è un compromesso in entrambi gli scenari e la denormalizzazione mette effettivamente a rischio l'integrità dei dati. Direi che la normalizzazione del database migliora effettivamente le prestazioni complessive del database piuttosto che avere un rapido aumento trascurabile delle prestazioni da una tabella denormalizzata.
Rudy Garcia,

Dato che la discussione riguarda specificamente le relazioni 1: 1, dividere le tabelle non è un'attività di normalizzazione , giusto? Se non ci sono informazioni duplicate, è normale anche quando è una singola tabella. (Beh, potrebbe non soddisfare la 3NFnormalizzazione, quindi beneficia di una seconda tabella per risolverlo, ma non sembra essere ciò che OP si riferisce alle altre tabelle.)
ToolmakerSteve

14

Fare tutto di quei tavoli hanno una 1-to-1relazione? Ad esempio, ogni riga utente avrà solo una riga corrispondente in user_statso user_levels? In tal caso, potrebbe avere senso combinarli in un'unica tabella. Se la relazione non lo è 1 to 1 , probabilmente non avrebbe senso combinarli (denormalizzarli).

Averli in tabelle separate rispetto a una tabella probabilmente avrà scarso effetto sulle prestazioni, a meno che tu non abbia centinaia di migliaia o milioni di record utente. L'unico vero vantaggio che otterrai è semplificare le tue query combinandole.

ETA:

Se la tua preoccupazione è di avere troppe colonne , pensa a quali elementi usi normalmente insieme e combinali , lasciando il resto in una tabella separata (o più tabelle separate se necessario).

Se guardi al modo in cui usi i dati, la mia ipotesi è che scoprirai che qualcosa come l'80% delle tue query utilizza il 20% di quei dati e il restante 80% dei dati viene utilizzato solo occasionalmente. Combina quel 20% usato di frequente in una tabella e lascia l'80% che non usi spesso in tabelle separate e probabilmente avrai un buon compromesso.


Sì, ogni tabella ha solo 1 riga per ogni utente, semplicemente per risparmiare il mal di testa di gestire molti dati duplicati. Questo è il motivo per cui penso che un tavolo sia adatto. Se i dati utente si estendessero su più righe, mi aspetto che quelle tabelle siano separate dalla tabella utente principale.
Peter Craig

1
Se ogni tabella ha una relazione 1 a 1, una tabella sarebbe più facile da usare. In questo caso non è necessario dividere la tabella. La divisione della tabella suggerisce che ci sono più di 1 riga, il che potrebbe portare a un caso in cui un altro sviluppatore le tratterebbe in questo modo.
Richard L

Pensiero molto interessante sull'applicazione dell'80 / 20 alla progettazione di tabelle di database. Mi ha fatto pensare anche al design della classe OOP (sono principalmente uno sviluppatore Java) e mi chiedo se lo stesso potrebbe essere efficace lì (metti la funzionalità dell'applicazione primaria all'80% in una classe e il resto in altre classi).
Zack Macomber

1
@ZackMacomber - No, la suddivisione delle classi dovrebbe essere basata sulla località di riferimento . Il vantaggio della suddivisione in più classi consiste nel disegnare un bordo attorno a un'unità di funzionalità più piccola, in modo che sia più facile comprendere / testare / modificare e chiarire dove quell'unità interagisce con altre unità di funzionalità. L'obiettivo è mantenere la maggior parte delle connessioni (riferimenti, chiamate) all'interno di un'unità, con poche connessioni tra le unità . La definizione di diverse interfacce implementate dalla classe, con interfacce diverse per caso d'uso, può essere un utile primo passo verso tale suddivisione.
ToolmakerSteve

@ToolmakerSteve Buoni pensieri +1
Zack Macomber

9

La creazione di una tabella di grandi dimensioni va contro le entità del database relazionale. Non li combinerei tutti in una tabella. Otterrai più istanze di dati ripetuti. Se il tuo utente ha tre interessi, ad esempio, avrai 3 righe, con gli stessi dati utente solo per memorizzare i tre diversi interessi. Sicuramente scegli l'approccio a più tabelle "normalizzate". Vedi questa pagina Wiki per la normalizzazione del database.

Modifica: ho aggiornato la mia risposta, poiché hai aggiornato la tua domanda ... Sono d'accordo con la mia risposta iniziale ancora di più ora da ...

è probabile che gran parte di queste celle rimanga vuota

Se, ad esempio, un utente non aveva interessi, se normalizzi, semplicemente non avrai una riga nella tabella degli interessi per quell'utente. Se hai tutto in una tabella enorme, avrai colonne (e apparentemente molte di esse) che contengono solo NULL.

Ho lavorato per una società di telefonia in cui c'erano tonnellate di tabelle, ottenere dati potrebbe richiedere molti join. Quando le prestazioni di lettura di queste tabelle erano critiche, venivano create procedure che potevano generare una tabella piatta (cioè una tabella denormalizzata) che non richiedeva join, calcoli, ecc. A cui i rapporti potevano puntare. Questi sono stati poi utilizzati insieme a un agente del server SQL per eseguire il lavoro a determinati intervalli (ad esempio, una visualizzazione settimanale di alcune statistiche sarebbe stata eseguita una volta alla settimana e così via).


Mi piace questo approccio, perché i dati denormalizzati esistono solo temporaneamente, come un'istantanea di un momento nel tempo. Nessun problema di inserimento / modifica / eliminazione: buttalo via quando hai finito.
ToolmakerSteve

7

Perché non utilizzare lo stesso approccio di Wordpress avendo una tabella utenti con le informazioni utente di base che tutti hanno e quindi aggiungendo una tabella "user_meta" che può essere fondamentalmente qualsiasi coppia chiave e valore associata all'id utente. Quindi, se hai bisogno di trovare tutte le meta informazioni per l'utente, puoi semplicemente aggiungerle alla tua query. Inoltre, non dovresti sempre aggiungere la query in più se non è necessaria per cose come l'accesso. Il vantaggio di questo approccio lascia anche la tua tabella aperta all'aggiunta di nuove funzionalità ai tuoi utenti come memorizzare il loro handle di Twitter o ogni singolo interesse. Inoltre, non dovrai occuparti di un labirinto di ID associati perché hai una tabella che regola tutti i metadati e la limiterai a una sola associazione invece di 50.

Wordpress lo fa specificamente per consentire l'aggiunta di funzionalità tramite plug-in, consentendo quindi al progetto di essere più scalabile e non richiederà una revisione completa del database se è necessario aggiungere una nuova funzionalità.


La wp_usermetatabella di Wordpress cresce geometricamente. Ogni utente aggiunge X righe alla wp_usermetatabella, una riga per ogni metadata che vogliamo conservare per quell'utente. Se mantieni 8 campi personalizzati per ogni utente, significa che wp_usermeta sarà users * 8lungo le righe. Sembra che questo stia causando problemi di prestazioni, ma non sono sicuro se sia questo il problema o meno ...
terza persona

1
Ho potuto vedere come ciò potrebbe causare problemi di prestazioni se hai decine di migliaia di utenti. Fondamentalmente il database dovrebbe cercare tra 10000 * 8 voci nella meta tabella dell'utente per trovare quelle che stai cercando. Tuttavia, se interroghi i dati Meta solo quando necessario, penso che le tue prestazioni sarebbero migliori. Se chiedi sempre i metadati anche quando non ne hai bisogno, potresti avere problemi. Se hai sempre bisogno dei metadati, forse dividere le tabelle non è l'approccio migliore.
Rudy Garcia

1
Proprio ieri ci siamo occupati di un tema WP che caricava tutti gli utenti (utilizzando get_users()) solo per calcolare l'impaginazione. Una volta corretto il codice per utilizzare invece una SELECT COUNT(…)query per l'impaginazione, il tempo di caricamento della pagina è passato da 28 secondi a circa 400 ms. Mi chiedo ancora come si confronta il rendimento con le tabelle unite o una singola tabella piatta ... Ho avuto problemi a trovare le metriche delle prestazioni sul Web.
thirdender

Pensando al mio commento precedente sembrerebbe che dividere la tabella sia ancora efficiente a meno che per qualche motivo, come l'esempio di impaginazione sopra, non sia necessario selezionare tutti gli utenti. Sebbene tu stia recuperando tutte le meta informazioni, avresti comunque 80k voci nella tabella usermeta. È molto da cercare. Forse qualcuno potrebbe testare quale sia un approccio migliore eseguendo uno script su entrambe le implementazioni ed eseguirlo 100 volte per ottenere la media, potrei semplicemente farlo.
Rudy Garcia

1
L'ho letto di nuovo solo oggi e mi sono reso conto che il mio commento su 10000 * 8 voci è vero, tuttavia il modo in cui funziona un database dovrebbe renderlo per lo più un non problema. Se per qualche motivo stavi catturando tutti i 10000 utenti E poi anche le loro meta informazioni, questo sarebbe ridicolo. Non riesco a pensare a nessuno scenario in cui lo vorresti. Un database recupererà facilmente il meta per un singolo utente alla velocità della luce, anche se a causa delle chiavi esterne e dell'indicizzazione. Supponendo che il tuo modello db sia impostato correttamente.
Rudy Garcia

5

Penso che questa sia una di quelle situazioni "dipende". Avere più tabelle è più pulito e probabilmente teoricamente migliore. Ma quando devi unire 6-7 tabelle per ottenere informazioni su un singolo utente, potresti iniziare a ripensare a quell'approccio.


1

Direi che dipende da cosa significano veramente le altre tabelle. Un user_details contiene più di 1 / utenti in più e così via. Il livello di normalizzazione più adatto alle tue esigenze dipende dalle tue esigenze.

Se hai una tabella con un buon indice, probabilmente sarebbe più veloce. Ma d'altra parte probabilmente più difficile da mantenere.

A me sembra che tu possa saltare User_Details poiché probabilmente è una relazione 1 a 1 con gli utenti. Ma il resto è probabilmente un sacco di righe per utente?

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.