Was ist effizienter: Mehrere MySQL-Tabellen oder eine große Tabelle?

103

Ich speichere verschiedene Benutzerdetails in meiner MySQL-Datenbank. Ursprünglich wurde es in verschiedenen Tabellen eingerichtet, was bedeutet, dass Daten mit UserIds verknüpft sind und über manchmal komplizierte Aufrufe ausgegeben werden, um die Daten nach Bedarf anzuzeigen und zu bearbeiten. Beim Einrichten eines neuen Systems ist es fast sinnvoll, alle diese Tabellen in einer großen Tabelle mit verwandten Inhalten zu kombinieren.

  • Wird dies eine Hilfe oder ein Hindernis sein?
  • Überlegungen zur Geschwindigkeit beim Aufrufen, Aktualisieren oder Suchen / Bearbeiten?

Hier ist ein Beispiel für einige meiner Tabellenstrukturen:

  • Benutzer - Benutzer-ID, Benutzername, E-Mail, verschlüsseltes Passwort, Registrierungsdatum, IP
  • user_details - Cookie-Daten, Name, Adresse, Kontaktdaten, Zugehörigkeit, demografische Daten
  • user_activity - Beiträge, zuletzt online, letzte Anzeige
  • user_settings - Einstellungen für die Profilanzeige
  • user_interests - Werbung für zielgerichtete Variablen
  • user_levels - Zugriffsrechte
  • user_stats - Treffer, Tallies

Bearbeiten: Ich habe bisher alle Antworten positiv bewertet, sie haben alle Elemente, die im Wesentlichen meine Frage beantworten.

Die meisten Tabellen haben eine 1: 1-Beziehung, was der Hauptgrund für die Denormalisierung war.

Wird es Probleme geben, wenn sich die Tabelle über mehr als 100 Spalten erstreckt, wenn ein großer Teil dieser Zellen wahrscheinlich leer bleibt?

Peter Craig
quelle
Diese andere Frage könnte auch hilfreich sein
Mosty Mostacho

Antworten:

65

Mehrere Tabellen helfen auf folgende Weise / in folgenden Fällen:

(a) Wenn verschiedene Personen Anwendungen mit unterschiedlichen Tabellen entwickeln, ist es sinnvoll, diese aufzuteilen.

(b) Wenn Sie verschiedenen Personen für verschiedene Teile der Datenerfassung unterschiedliche Befugnisse erteilen möchten, ist es möglicherweise bequemer, sie aufzuteilen. (Natürlich können Sie auch Ansichten definieren und entsprechende Berechtigungen erteilen.)

(c) Um Daten an verschiedene Orte zu verschieben, insbesondere während der Entwicklung, kann es sinnvoll sein, Tabellen zu verwenden, die zu kleineren Dateigrößen führen.

(d) Ein geringerer Platzbedarf kann Komfort bieten, wenn Sie Anwendungen für die spezifische Datenerfassung einer einzelnen Entität entwickeln.

(e) Es ist eine Möglichkeit: Was Sie als Einzelwertdaten dachten, kann sich in Zukunft als wirklich mehrere Werte herausstellen. zB ist das Kreditlimit ab sofort ein einzelnes Wertefeld. Aber morgen können Sie entscheiden, die Werte als (Datum von, Datum bis, Kreditwert) zu ändern. Geteilte Tabellen könnten jetzt nützlich sein.

Ich würde für mehrere Tabellen stimmen - mit entsprechend aufgeteilten Daten.

Viel Glück.

user115905
quelle
3
@RohitKhatri: Nach meinem besten Wissen erhöht das Vorhandensein mehrerer Tabellen in den meisten Fällen die Leistung.
Hari Harker
1
@ HariHarker Danke für deine Antwort, aber ich habe herausgefunden, dass es von deinem Zugriffsmuster abhängt.
Rohit Khatri
Bis vor kurzem habe ich immer alle Daten in einer Tabelle gespeichert, aber wenn ich mir das überlege, hat es viele Vorteile, Daten in Bezug auf Leistung (abhängig vom Anwendungsfall natürlich) und Semantik (einige Daten sind besser in a gruppiert) aufzuteilen andere Tabelle) und Entwicklung. Zum Beispiel entwickle ich gerade ein benutzerdefiniertes ERP-System auf einem Legacy-System. Ich musste die alten Datenbanktabellen um zusätzliche Spalten erweitern. Ich habe beschlossen, neue Tabellen für die neuen Daten zu erstellen. Einige neue Funktionen sind praktisch für das Legacy-System, und jetzt kann ich sie problemlos integrieren, ohne zu viele der alten Abfragen neu schreiben zu müssen
Ogier Schelvis,
35

Das Kombinieren der Tabellen wird als Denormalisieren bezeichnet.

Es kann (oder auch nicht) hilfreich sein, einige Abfragen (die viele JOINs machen) schneller auszuführen, auf Kosten der Erstellung einer Wartungshölle.

MySQList in der Lage, nur JOINMethode zu verwenden, nämlich NESTED LOOPS.

Dies bedeutet, dass für jeden Datensatz in der MySQLTreibertabelle ein übereinstimmender Datensatz in der Treibertabelle in einer Schleife gefunden wird.

Das Auffinden eines Datensatzes ist ein ziemlich kostspieliger Vorgang, der Dutzende Male so lange dauern kann wie das reine Scannen von Datensätzen.

Wenn Sie alle Ihre Datensätze in eine Tabelle verschieben, können Sie diesen Vorgang vermeiden. Die Tabelle selbst wird jedoch größer und der Tabellenscan dauert länger.

Wenn Sie viele Datensätze in anderen Tabellen haben, kann eine Erhöhung des Tabellenscans die Vorteile der nacheinander gescannten Datensätze übergewichten.

Die Wartungshölle hingegen ist garantiert.

Quassnoi
quelle
1
Wenn Sie 10000 Benutzer haben und eine Verknüpfung mit einer Datenbank herstellen, die mit Fremdschlüsseln korrekt eingerichtet wurde, sollten Sie die intensive Suche nur benötigen, indem Sie beispielsweise * von Benutzern auswählen, bei denen name = "bob". Sobald Sie Bob haben, verwenden Sie einen Index, um die mit Bob verbundenen Tabellen zu finden. Dies ist erheblich schneller, da Sie die Bob-ID verwenden. Dies geschieht unabhängig davon, ob Sie einen Join in Ihrer Abfrage durchführen oder Bob abfragen und dann eine Tabelle separat abfragen. Natürlich basiert Ihre zweite Anfrage hoffentlich auf der ID von Bob und nicht auf etwas anderem.
Rudy Garcia
17

Sind alle 1: 1-Beziehungen? Ich meine, wenn ein Benutzer beispielsweise verschiedenen Benutzerebenen angehören könnte oder wenn die Benutzerinteressen als mehrere Datensätze in der Benutzerinteressentabelle dargestellt werden, würde das Zusammenführen dieser Tabellen sofort nicht in Frage kommen.

In Bezug auf frühere Antworten zur Normalisierung muss gesagt werden, dass die Regeln für die Datenbanknormalisierung die Leistung völlig außer Acht gelassen haben und sich nur mit einem ordentlichen Datenbankdesign befassen. Das ist oft das, was Sie erreichen möchten, aber es gibt Zeiten, in denen es sinnvoll ist, aktiv zu denormalisieren, um Leistung zu erzielen.

Alles in allem würde ich sagen, dass die Frage darauf hinausläuft, wie viele Felder in den Tabellen vorhanden sind und wie oft auf sie zugegriffen wird. Wenn Benutzeraktivitäten oft nicht sehr interessant sind, kann es aus Leistungs- und Wartungsgründen nur lästig sein, sie immer auf demselben Datensatz zu haben . Wenn auf einige Daten, wie z. B. Einstellungen, sehr häufig zugegriffen wird, aber einfach zu viele Felder enthalten, ist es möglicherweise auch nicht bequem, die Tabellen zusammenzuführen. Wenn Sie nur an der Leistungssteigerung interessiert sind, können Sie andere Ansätze in Betracht ziehen, z. B. die Einstellungen getrennt zu halten, sie jedoch in einer eigenen Sitzungsvariablen zu speichern, damit Sie die Datenbank nicht sehr oft nach ihnen abfragen müssen.

David Hedlund
quelle
Ich muss Ihrem Kommentar völlig widersprechen, dass sich die Normalisierung nur auf Sauberkeit konzentriert und die Leistung völlig außer Acht lässt. In beiden Szenarien gibt es einen Kompromiss, und die Denormalisierung gefährdet tatsächlich die Datenintegrität. Ich würde sagen, dass die Normalisierung Ihrer Datenbank tatsächlich die Gesamtleistung der Datenbank verbessert, anstatt eine schnelle, vernachlässigbare Leistungssteigerung durch eine denormalisierte Tabelle zu erzielen.
Rudy Garcia
Angesichts der Tatsache, dass es in der Diskussion speziell um 1: 1-Beziehungen geht, ist das Aufteilen der Tabellen keine Normalisierungsaufgabe , oder? Wenn keine doppelten Informationen vorhanden sind, ist dies normal, auch wenn es sich um eine einzelne Tabelle handelt. (Nun, es könnte die 3NFNormalisierung nicht erfüllen , also profitieren Sie von einer zweiten Tabelle, um das zu beheben, aber das scheint nicht das zu sein, worauf sich OP für die anderen Tabellen bezieht.)
ToolmakerSteve
14

Haben alle diese Tabellen eine 1-to-1Beziehung? Hat beispielsweise jede Benutzerzeile nur eine entsprechende Zeile in user_statsoder user_levels? In diesem Fall ist es möglicherweise sinnvoll, sie in einer Tabelle zu kombinieren. Wenn die Beziehung nicht besteht 1 to 1 , wäre es wahrscheinlich nicht sinnvoll, sie zu kombinieren (zu denormalisieren).

Wenn Sie sie in separaten Tabellen gegenüber einer Tabelle haben, hat dies wahrscheinlich nur geringe Auswirkungen auf die Leistung, es sei denn, Sie haben Hunderttausende oder Millionen von Benutzerdatensätzen. Der einzige wirkliche Vorteil besteht darin, dass Sie Ihre Abfragen vereinfachen, indem Sie sie kombinieren.

ETA:

Wenn Sie Bedenken haben, zu viele Spalten zu haben , überlegen Sie, welche Elemente Sie normalerweise zusammen verwenden, und kombinieren Sie diese. Lassen Sie den Rest in einer separaten Tabelle (oder bei Bedarf in mehreren separaten Tabellen).

Wenn Sie sich die Art und Weise ansehen, wie Sie die Daten verwenden, werden Sie wahrscheinlich feststellen, dass etwa 80% Ihrer Abfragen 20% dieser Daten verwenden, während die restlichen 80% der Daten nur gelegentlich verwendet werden. Kombinieren Sie die häufig verwendeten 20% in einer Tabelle und lassen Sie die 80%, die Sie nicht oft verwenden, in separaten Tabellen, und Sie haben wahrscheinlich einen guten Kompromiss.

Eric Petroelje
quelle
Ja, jede Tabelle enthält nur 1 Zeile für jeden Benutzer, um die Kopfschmerzen bei der Verwaltung vieler doppelter Daten zu vermeiden. Deshalb denke ich, dass ein Tisch passt. Wenn Benutzerdaten mehrere Zeilen umfassen würden, würde ich erwarten, dass diese Tabellen von der Hauptbenutzertabelle getrennt sind.
Peter Craig
1
Wenn jede Tabelle eine 1: 1-Beziehung hat, ist eine Tabelle einfacher zu verwenden. In diesem Fall muss die Tabelle nicht aufgeteilt werden. Das Aufteilen der Tabelle deutet darauf hin, dass mehr als eine Zeile vorhanden ist, was dazu führen kann, dass ein anderer Entwickler sie so behandelt.
Richard L
Sehr interessanter Gedanke, 80/20 auf das Design von Datenbanktabellen anzuwenden. Ich habe auch über das Design von OOP-Klassen nachgedacht (ich bin hauptsächlich ein Java-Entwickler) und mich gefragt, ob dies dort auch effektiv sein könnte (primäre 80% -Anwendungsfunktionalität in einer Klasse und den Rest in anderen Klassen).
Zack Macomber
1
@ZackMacomber - Nein, die Aufteilung der Klassen sollte auf der Referenzlokalität basieren . Der Vorteil der Aufteilung in mehrere Klassen besteht darin, dass eine kleinere Funktionseinheit umrandet wird, damit sie leichter zu verstehen / zu testen / zu ändern ist und klar ist, wo diese Einheit mit anderen Funktionseinheiten interagiert. Das Ziel ist es, die meisten Verbindungen (Referenzen, Anrufe) innerhalb einer Einheit zu halten, mit wenigen Verbindungen zwischen Einheiten . Definieren von mehreren Schnittstellen , dass die Klasse implementiert, mit unterschiedlichen Schnittstellen pro Anwendungsfall kann ein nützlicher erster Schritt in diese Richtung Spaltung sein.
ToolmakerSteve
@ ToolmakerSteve Gute Gedanken +1
Zack Macomber
9

Das Erstellen einer massiven Tabelle widerspricht den Prinzipien relationaler Datenbanken. Ich würde nicht alle zu einem Tisch zusammenfassen. Sie werden mehrere Instanzen wiederholter Daten erhalten. Wenn Ihr Benutzer beispielsweise drei Interessen hat, haben Sie drei Zeilen mit denselben Benutzerdaten, um nur die drei verschiedenen Interessen zu speichern. Entscheiden Sie sich auf jeden Fall für den Ansatz mit mehreren "normalisierten" Tabellen. Auf dieser Wiki-Seite finden Sie Informationen zur Datenbanknormalisierung.

Bearbeiten: Ich habe meine Antwort aktualisiert, da Sie Ihre Frage aktualisiert haben ... Ich stimme meiner ursprünglichen Antwort jetzt noch mehr zu, da ...

Ein großer Teil dieser Zellen bleibt wahrscheinlich leer

Wenn zum Beispiel ein Benutzer keine Interessen hatte, wenn Sie normalisieren, haben Sie einfach keine Zeile in der Interessentabelle für diesen Benutzer. Wenn Sie alles in einer massiven Tabelle haben, haben Sie Spalten (und anscheinend viele davon), die nur NULL-Werte enthalten.

Ich habe für eine Telefoniefirma gearbeitet, in der es unzählige Tabellen gab. Das Abrufen von Daten kann viele Verknüpfungen erfordern. Wenn die Leistung beim Lesen aus diesen Tabellen kritisch war, wurden Verfahren erstellt, die eine flache Tabelle (dh eine denormalisierte Tabelle) generieren konnten, für die keine Verknüpfungen, Berechnungen usw. erforderlich waren, auf die Berichte verweisen konnten. Diese wurden dann in Verbindung mit einem SQL Server-Agenten verwendet, um den Job in bestimmten Intervallen auszuführen (dh eine wöchentliche Ansicht einiger Statistiken würde einmal pro Woche usw. ausgeführt).


quelle
Ich mag diesen Ansatz, weil die denormalisierten Daten nur vorübergehend als Momentaufnahme eines bestimmten Zeitpunkts vorhanden sind. Keine Probleme beim Einfügen / Ändern / Löschen - werfen Sie es einfach weg, wenn Sie fertig sind.
ToolmakerSteve
7

Warum nicht den gleichen Ansatz verwenden? Wordpress verwendet eine Benutzertabelle mit grundlegenden Benutzerinformationen, die jeder hat, und fügt dann eine "user_meta" -Tabelle hinzu, die im Grunde ein beliebiges Schlüssel-Wert-Paar sein kann, das der Benutzer-ID zugeordnet ist. Wenn Sie also alle Metainformationen für den Benutzer finden müssen, können Sie diese einfach zu Ihrer Abfrage hinzufügen. Sie müssten auch nicht immer die zusätzliche Abfrage hinzufügen, wenn sie nicht zum Beispiel für die Anmeldung benötigt wird. Der Vorteil dieses Ansatzes lässt Ihre Tabelle auch offen, um Ihren Benutzern neue Funktionen hinzuzufügen, z. B. das Speichern ihres Twitter-Handles oder jedes einzelne Interesse. Sie müssen sich auch nicht mit einem Labyrinth zugeordneter IDs befassen, da Sie eine Tabelle haben, die alle Metadaten regelt, und Sie beschränken sie auf nur eine Zuordnung anstelle von 50.

Wordpress tut dies speziell, um das Hinzufügen von Funktionen über Plugins zu ermöglichen, sodass Ihr Projekt skalierbarer wird und keine vollständige Datenbanküberholung erforderlich ist, wenn Sie eine neue Funktion hinzufügen müssen.

Rudy Garcia
quelle
Die Wordpress- wp_usermetaTabelle wächst geometrisch. Jeder Benutzer fügt der wp_usermetaTabelle X Zeilen hinzu , eine Zeile für jede Metainformation, die wir für diesen Benutzer behalten möchten. Wenn Sie 8 benutzerdefinierte Felder für jeden Benutzer behalten, bedeutet dies, dass wp_usermeta users * 8Zeilen lang ist. Dies scheint Leistungsprobleme zu verursachen, aber ich bin nicht sicher, ob das das Problem ist oder nicht ...
Thirdender
1
Ich konnte sehen, wie dies zu Leistungsproblemen führen kann, wenn Sie Zehntausende von Benutzern haben. Grundsätzlich müsste die Datenbank 10000 * 8 Einträge in der Benutzer-Metatabelle durchsuchen, um diejenigen zu finden, nach denen Sie suchen. Wenn Sie jedoch die Metadaten nur bei Bedarf abfragen, würde ich denken, dass Ihre Leistung besser wäre. Wenn Sie immer nach den Metadaten fragen, auch wenn Sie sie nicht benötigen, können Probleme auftreten. Wenn Sie die Metadaten immer benötigen, ist das Aufteilen der Tabellen möglicherweise nicht der beste Ansatz.
Rudy Garcia
1
Erst gestern haben wir uns mit einem WP-Thema befasst, das alle Benutzer (mit get_users()) geladen hat, nur um die Paginierung zu berechnen. Nachdem wir den Code korrigiert hatten, um SELECT COUNT(…)stattdessen eine Abfrage für die Paginierung zu verwenden, stieg die Ladezeit der Seite von 28 Sekunden auf etwa 400 ms. Ich frage mich immer noch, wie die Leistung im Vergleich zu verknüpften Tabellen oder einer einzelnen flachen Tabelle ist. Ich hatte Probleme, Leistungsmetriken im Web zu finden.
Thirdender
Wenn ich an meinen vorherigen Kommentar denke, scheint es, dass das Aufteilen der Tabelle immer noch effizient ist, es sei denn, Sie müssten aus irgendeinem Grund, wie im obigen Beispiel der Paginierung, alle Benutzer auswählen. Wenn Sie jedoch alle Metainformationen abrufen, befinden sich immer noch 80.000 Einträge in der Usermeta-Tabelle. Das ist viel zu durchsuchen. Vielleicht könnte jemand testen, was ein besserer Ansatz ist, indem er ein Skript auf beiden Implementierungen ausführt und es 100 Mal ausführt, um den Durchschnitt zu erhalten. Ich könnte das einfach tun.
Rudy Garcia
1
Ich habe dies erst heute noch einmal durchgelesen und festgestellt, dass mein Kommentar zu 10000 * 8 Einträgen wahr ist, aber die Art und Weise, wie eine Datenbank funktioniert, sollte es größtenteils kein Problem machen. Wenn Sie aus irgendeinem Grund alle 10000 Benutzer UND dann auch deren Metainformationen greifen würden, wäre dies lächerlich. Ich kann mir kein Szenario vorstellen, in dem Sie das wollen würden. Eine Datenbank kann das Meta für einen einzelnen Benutzer aufgrund von Fremdschlüsseln und Indizierung blitzschnell abrufen. Vorausgesetzt, Ihr Datenbankmodell ist korrekt eingerichtet.
Rudy Garcia
5

Ich denke, dies ist eine dieser Situationen, in denen es darauf ankommt. Mehrere Tische zu haben ist sauberer und wahrscheinlich theoretisch besser. Wenn Sie jedoch 6-7 Tabellen verbinden müssen, um Informationen zu einem einzelnen Benutzer zu erhalten, können Sie diesen Ansatz überdenken.

Tundey
quelle
1

Ich würde sagen, es hängt davon ab, was die anderen Tabellen wirklich bedeuten. Enthält ein user_details mehr als 1 weitere / users und so weiter? Welcher Normalisierungsgrad für Ihre Anforderungen am besten geeignet ist, hängt von Ihren Anforderungen ab.

Wenn Sie eine Tabelle mit einem guten Index haben, wäre dies wahrscheinlich schneller. Aber auf der anderen Seite wahrscheinlich schwieriger zu pflegen.

Für mich sieht es so aus, als könnten Sie User_Details überspringen, da es sich wahrscheinlich um eine 1: 1-Beziehung zu Users handelt. Aber der Rest sind wahrscheinlich viele Zeilen pro Benutzer?

Richard L.
quelle