Speichern von JSON in der Datenbank im Vergleich zu einer neuen Spalte für jeden Schlüssel

211

Ich implementiere das folgende Modell zum Speichern benutzerbezogener Daten in meiner Tabelle - ich habe 2 Spalten - uid(Primärschlüssel) und eine metaSpalte, in der andere Daten über den Benutzer im JSON-Format gespeichert werden.

 uid   | meta
--------------------------------------------------
 1     | {name:['foo'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------
 2     | {name:['sann'], 
       |  emailid:['[email protected]','[email protected]']}
--------------------------------------------------

Ist dies ein besserer Weg (Performance-weise, Design-weise) als das Ein-Spalt-per-Eigenschaft - Modell, in dem die Tabelle wie viele Spalten hat uid, name, emailid.

Was mir am ersten Modell gefällt, ist, dass Sie so viele Felder wie möglich hinzufügen können, ohne Einschränkungen.

Außerdem habe ich mich gefragt, nachdem ich das erste Modell implementiert habe. Wie führe ich eine Abfrage durch, z. B. möchte ich alle Benutzer abrufen, die einen Namen wie "foo" haben?

Frage - Was ist der bessere Weg, um benutzerbezogene Daten (unter Berücksichtigung der Tatsache, dass die Anzahl der Felder nicht festgelegt ist) mit - JSON oder Spalte pro Feld in der Datenbank zu speichern? Wenn das erste Modell implementiert ist, wie kann die Datenbank wie oben beschrieben abgefragt werden? Sollte ich beide Modelle verwenden, indem ich alle Daten, die von einer Abfrage durchsucht werden können, in einer separaten Zeile und die anderen Daten in JSON speichere (ist eine andere Zeile)?


Aktualisieren

Ist es ratsam, beide Modelle zu verwenden, da es nicht zu viele Spalten gibt, in denen ich suchen muss? Schlüssel pro Spalte für die Daten, die ich suchen muss, und JSON für andere (in derselben MySQL-Datenbank)?

ShuklaSannidhya
quelle
40
tolle Frage! aber warum hast du keine Antwort akzeptiert? das würde anderen Benutzern (wie mir) helfen
Sahar Ch.

Antworten:

197

Aktualisiert am 4. Juni 2017

Angesichts der Tatsache, dass diese Frage / Antwort an Popularität gewonnen hat, dachte ich, dass es ein Update wert ist.

Als diese Frage ursprünglich gestellt wurde, hatte MySQL keine Unterstützung für JSON-Datentypen und die Unterstützung in PostgreSQL steckte noch in den Kinderschuhen. Seit 5.7 unterstützt MySQL jetzt einen JSON-Datentyp (in einem binären Speicherformat), und PostgreSQL JSONB ist erheblich ausgereift. Beide Produkte bieten performante JSON-Typen, in denen beliebige Dokumente gespeichert werden können, einschließlich der Unterstützung für die Indizierung bestimmter Schlüssel des JSON-Objekts.

Ich stehe jedoch weiterhin zu meiner ursprünglichen Aussage, dass Ihre Standardeinstellung bei Verwendung einer relationalen Datenbank weiterhin Spalte pro Wert sein sollte. Relationale Datenbanken basieren immer noch auf der Annahme, dass die darin enthaltenen Daten ziemlich gut normalisiert sind. Der Abfrageplaner verfügt über bessere Optimierungsinformationen beim Betrachten von Spalten als beim Betrachten von Schlüsseln in einem JSON-Dokument. Fremdschlüssel können zwischen Spalten erstellt werden (jedoch nicht zwischen Schlüsseln in JSON-Dokumenten). Wichtig: Wenn der Großteil Ihres Schemas volatil genug ist, um die Verwendung von JSON zu rechtfertigen, sollten Sie zumindest überlegen, ob eine relationale Datenbank die richtige Wahl ist.

Allerdings sind nur wenige Anwendungen perfekt relational oder dokumentenorientiert. Die meisten Anwendungen haben eine Mischung aus beiden. Hier sind einige Beispiele, bei denen ich JSON persönlich in einer relationalen Datenbank als nützlich empfunden habe:

  • Beim Speichern von E-Mail-Adressen und Telefonnummern für einen Kontakt ist das Speichern als Werte in einem JSON-Array viel einfacher als das Verwalten mehrerer separater Tabellen

  • Speichern beliebiger Benutzereinstellungen für Schlüssel / Werte (wobei der Wert boolesch, textuell oder numerisch sein kann und Sie keine separaten Spalten für verschiedene Datentypen haben möchten)

  • Speichern von Konfigurationsdaten ohne definiertes Schema (wenn Sie Zapier oder IFTTT erstellen und Konfigurationsdaten für jede Integration speichern müssen)

Ich bin mir sicher, dass es auch andere gibt, aber dies sind nur einige kurze Beispiele.

Ursprüngliche Antwort

Wenn Sie wirklich so viele Felder hinzufügen möchten, wie Sie möchten, ohne Einschränkung (außer einer willkürlichen Beschränkung der Dokumentgröße), sollten Sie eine NoSQL-Lösung wie MongoDB in Betracht ziehen.

Für relationale Datenbanken: Verwenden Sie eine Spalte pro Wert. Das Einfügen eines JSON-Blobs in eine Spalte macht das Abfragen praktisch unmöglich (und schmerzhaft langsam, wenn Sie tatsächlich eine Abfrage finden, die funktioniert).

Relationale Datenbanken nutzen bei der Indizierung Datentypen und sollen mit einer normalisierten Struktur implementiert werden.

Als Randnotiz: Dies bedeutet nicht, dass Sie JSON niemals in einer relationalen Datenbank speichern sollten. Wenn Sie echte Metadaten hinzufügen oder wenn Ihr JSON Informationen beschreibt, die nicht abgefragt werden müssen und nur zur Anzeige verwendet werden, ist es möglicherweise übertrieben, eine separate Spalte für alle Datenpunkte zu erstellen.

Colin M.
quelle
1
Ist es ratsam, beide Modelle zu verwenden, da es nicht zu viele Spalten gibt, in denen ich suchen muss? Schlüssel pro Spalte für die Daten, die ich suchen muss, und JSON für andere (in derselben MySQL-Datenbank)?
ShuklaSannidhya
3
@Sann Sie sollten eine Spalte pro Wert für Daten verwenden, die Sie häufig lesen oder abfragen möchten . Das Einfügen eines Namens in JSON ist nicht sinnvoll, da Sie ihn wahrscheinlich sehr oft benötigen, auch wenn Sie wahrscheinlich nicht darauf basierend abfragen . Das ist eine Menge verschwenderischer Dekodierung auf Ihrer Anwendungsseite. Wenn Sie nicht wirklich das Gefühl haben, dass Ihre Daten besser als JSON dargestellt werden (und vertrauen Sie mir, wahrscheinlich nicht), sollten Sie nicht darauf zurückgreifen.
Colin M
5
" virtually impossible to query" - heute psql ermöglicht es Ihnen, seine jsonb
ted
1
@ted wahr. Zum Zeitpunkt des Schreibens dieser Antwort war diese jedoch nicht wirklich verfügbar. Diese Frage bezieht sich auch auf MySQL, in dem keine Fähigkeit vorhanden ist.
Colin M
3
@ColinM, ja, mir ist klar, dass mein Kommentar 3 Jahre jünger ist als dein Beitrag. Der Grund, warum ich es verlassen habe, ist, dass es für andere hilfreich sein und Entscheidungen ändern kann. Wie für den Verweis auf MySQL: könnte wahr sein, aber "For relational databases"in Ihrer Antwort = P
ted
68

Wie die meisten Dinge "kommt es darauf an". Es ist an und für sich nicht richtig oder falsch / gut oder schlecht, Daten in Spalten oder JSON zu speichern. Es hängt davon ab, was Sie später damit machen müssen. Wie werden Sie voraussichtlich auf diese Daten zugreifen? Müssen Sie auf andere Daten verweisen?

Andere Leute haben ziemlich gut geantwortet, was der technische Kompromiss ist.

Nicht viele Leute haben darüber gesprochen, dass sich Ihre App und Funktionen im Laufe der Zeit weiterentwickeln und wie sich diese Datenspeicherungsentscheidung auf Ihr Team auswirkt.

Da eine der Versuchungen bei der Verwendung von JSON darin besteht, die Migration von Schemata zu vermeiden. Wenn das Team nicht diszipliniert ist, ist es sehr einfach, ein weiteres Schlüssel / Wert-Paar in ein JSON-Feld einzufügen. Es gibt keine Migration dafür, niemand erinnert sich, wofür es ist. Es gibt keine Validierung.

Mein Team verwendete JSON neben traditionellen Säulen in Postgres und zuerst war es das Beste seit geschnittenem Brot. JSON war attraktiv und leistungsstark, bis wir eines Tages feststellten, dass Flexibilität mit Kosten verbunden war und plötzlich ein echtes Problem darstellt. Manchmal schleicht sich dieser Punkt sehr schnell ein und dann wird es schwierig, ihn zu ändern, weil wir so viele andere Dinge auf dieser Entwurfsentscheidung aufgebaut haben.

Überstunden, das Hinzufügen neuer Funktionen und das Vorhandensein der Daten in JSON führten zu komplizierteren Abfragen als die, die hinzugefügt worden wären, wenn wir uns an herkömmliche Spalten gehalten hätten. Also fingen wir an, bestimmte Schlüsselwerte wieder in Spalten zu fischen, damit wir Verknüpfungen herstellen und Vergleiche zwischen Werten anstellen konnten. Schlechte Idee. Jetzt hatten wir Duplikate. Ein neuer Entwickler würde an Bord kommen und verwirrt sein? In welchen Wert sollte ich zurücksparen? Der JSON oder die Spalte?

Die JSON-Felder wurden zu Müllschubladen für kleine Stücke von diesem und jenem. Keine Datenvalidierung auf Datenbankebene, keine Konsistenz oder Integrität zwischen Dokumenten. Das hat all diese Verantwortung in die App übertragen, anstatt eine harte Typprüfung und Einschränkungsprüfung aus herkömmlichen Spalten zu erhalten.

Rückblickend erlaubte uns JSON, sehr schnell zu iterieren und etwas aus der Tür zu holen. Es war toll. Nachdem wir jedoch eine bestimmte Teamgröße erreicht hatten, konnten wir uns aufgrund der Flexibilität auch mit einem langen Seil technischer Schulden aufhängen, was den späteren Fortschritt der Feature-Entwicklung verlangsamte. Mit Vorsicht verwenden.

Überlegen Sie lange und gründlich, wie Ihre Daten aussehen. Es ist das Fundament Ihrer App. Wie werden die Daten im Laufe der Zeit verwendet? Und wie ist es wahrscheinlich zu ändern?

Homan
quelle
6
"Seine Flexibilität hat es uns auch ermöglicht, uns mit einem langen Seil technischer Schulden aufzuhängen", sehr schöne Metapher!
Antoine Gallix
Nach vielen Jahren der Entwicklung und der Arbeit mit verschiedenen Menschen werde ich das Gleiche schreiben, wenn ich über dieses Thema schreiben sollte. Mittlerweile gibt es so viele Entwickler, dass viele von ihnen trotz jahrelanger Erfahrung nicht wirklich aufsteigen. Wir müssen alles einfach halten und für mich sind die beiden Dinge, die wir immer berücksichtigen müssen, um den Erfolg zu "rahmen", die Skalierbarkeit und Wartbarkeit des Codes.
JohnnyJaxs
27

Wirf es einfach raus, aber WordPress hat eine Struktur für diese Art von Sachen (zumindest war WordPress der erste Ort, an dem ich es beobachtet habe, es hat wahrscheinlich seinen Ursprung woanders).

Es erlaubt unbegrenzte Schlüssel und ist schneller zu suchen als ein JSON-Blob, aber nicht so schnell wie einige der NoSQL-Lösungen.

uid   |   meta_key    |   meta_val
----------------------------------
1         name            Frank
1         age             12
2         name            Jeremiah
3         fav_food        pizza
.................

BEARBEITEN

Zum Speichern von Verlauf / mehreren Schlüsseln

uid   | meta_id    |   meta_key    |   meta_val
----------------------------------------------------
1        1             name            Frank
1        2             name            John
1        3             age             12
2        4             name            Jeremiah
3        5             fav_food        pizza
.................

und über so etwas abfragen:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc
Adam
quelle
1
Ich wäre gespannt, ob eine NoSQL-Lösung wirklich eine bessere Leistung als eine relationale Abfrage für einen ordnungsgemäß indexierten Schlüssel aufweist. Ich würde vermuten, dass es in einem 1-Level-Beispiel wie diesem mehr oder weniger dasselbe sein sollte.
Bruno
+1. Ich habe es auch bemerkt! Aber es gibt Ihnen eine riesige Tabelle (in Zeilen). Sie können auch nicht mehrere Werte speichern, beispielsweise wenn der Benutzer seinen Namen ändert, aber ich möchte auch den alten Namen beibehalten. In diesem Fall benötige ich ein Datenmodell vom Typ JSON.
ShuklaSannidhya
@Sann, wenn Sie den alten Wert in JSON beibehalten möchten, müssen Sie auch den Schlüssel umbenennen: Sie können mit einem EAV (wie in diesem Beispiel dargestellt) oder JSON arbeiten. Es ist nicht besonders anders.
Bruno
Es gibt Ihnen zwar eine große Tabelle, aber bei doppelten Werten tritt bei JSON das gleiche Problem auf - Sie können keine doppelten Schlüssel auf derselben Ebene haben (z. B. zwei "Name" -Schlüssel) und erwarten ein vorhersehbares Verhalten.
Adam
Sicher, Sie können keine doppelten Schlüssel haben, aber diesem Schlüssel kann ein Array zugeordnet sein. Überprüfen Sie den emailidSchlüssel in dem Beispiel, das ich in meiner Frage gegeben habe.
ShuklaSannidhya
13

Der Nachteil des Ansatzes ist genau das, was Sie erwähnt haben:

es macht es SEHR langsam, Dinge zu finden, da Sie jedes Mal eine Textsuche durchführen müssen.

Der Wert pro Spalte entspricht stattdessen der gesamten Zeichenfolge.

Ihr Ansatz (JSON-basierte Daten) eignet sich für Daten, nach denen Sie nicht suchen müssen und die nur zusammen mit Ihren normalen Daten angezeigt werden müssen.

Bearbeiten: Zur Verdeutlichung gilt das oben Gesagte für klassische relationale Datenbanken. NoSQL verwendet JSON intern und ist wahrscheinlich eine bessere Option, wenn dies das gewünschte Verhalten ist.

Nick Andriopoulos
quelle
1
Du meinst also, ich sollte beides verwenden. Schlüssel pro Spalte für die Daten, die ich suchen muss, und JSON für andere, richtig?
ShuklaSannidhya
4
Ja. Auf diese Weise erhalten Sie die erforderliche Leistung, wenn Sie die Daten pro Spalte durchsuchen und den JSON-Blob abrufen, der bei Bedarf im Code verwendet werden soll.
Nick Andriopoulos
9

Grundsätzlich wird das erste Modell, das Sie verwenden, als dokumentbasierter Speicher bezeichnet. Sie sollten sich die beliebte dokumentbasierte NoSQL-Datenbank wie MongoDB und CouchDB ansehen . Grundsätzlich speichern Sie in dokumentbasierten Datenbanken Daten in JSON-Dateien und können diese JSON-Dateien abfragen.

Das zweite Modell ist die beliebte relationale Datenbankstruktur.

Wenn Sie eine relationale Datenbank wie MySQL verwenden möchten, würde ich Ihnen empfehlen, nur das zweite Modell zu verwenden. Es macht keinen Sinn, MySQL zu verwenden und Daten wie im ersten Modell zu speichern .

Um Ihre zweite Frage zu beantworten, gibt es keine Möglichkeit, Namen wie 'foo' abzufragen, wenn Sie das erste Modell verwenden .

Girish
quelle
Ist es ratsam, beide Modelle zu verwenden? Schlüssel pro Spalte für die Daten, die ich suchen muss, und JSON für andere (in derselben Datenbank)?
ShuklaSannidhya
@Sann - haha. Das ist Datenvervielfältigung. Sie müssen sicherstellen, dass beide Daten immer gleich sind. Selbst wenn sich die Daten zu irgendeinem Zeitpunkt unterscheiden, sind Ihre Daten nicht sauber und können zu ernsthaften Problemen führen. Also, meine Antwort ist NEIN
Girish
Redundanz ist jedoch nicht kostspielig, wenn die redundanten Daten klein sind. Beispielsweise gibt es nur zwei Felder, in denen ich suchen muss. Daher erstelle ich zwei neue Spalten für sie. [Vielleicht] entferne ich sie aus meinen JSON-Daten. [/ Vielleicht] . Das wird keine kostspielige Vervielfältigung sein, oder?
ShuklaSannidhya
Wenn Sie sich die Leistung ansehen, bieten MongoDB und CouchDB schnellere Lese- und Schreibvorgänge als MySQL, da sie in relationalen Datenbanken nicht viele Funktionen bieten, die in den meisten Anwendungsfällen nicht erforderlich sind.
Girish
Könnte der Vorteil nicht darin bestehen, JSON-Objekte / Rückrufe von einer API zu speichern? Anstatt beispielsweise die YouTube-API für URL, Daumen usw. aufzurufen, können Sie auch Ihre lokale Datenbank (MySQL, Lite usw.) nach dem JSON-Objekt abfragen. Ich weiß nicht, macht für mich Sinn, besonders wenn Sie versuchen, eine App zwischenzuspeichern oder schneller laufen zu lassen. Aber ich bin kein Profi: /
Markbratanov
4

Es scheint, dass Sie hauptsächlich zögern, ein relationales Modell zu verwenden oder nicht.

So wie es aussieht, würde Ihr Beispiel ziemlich gut zu einem relationalen Modell passen, aber das Problem kann natürlich auftreten, wenn Sie dieses Modell weiterentwickeln müssen.

Wenn Sie nur eine (oder einige vordefinierte) Attributebenen für Ihre Hauptentität (Benutzer) haben, können Sie weiterhin ein EAV-Modell (Entity Attribute Value) in einer relationalen Datenbank verwenden. (Dies hat auch Vor- und Nachteile.)

Wenn Sie davon ausgehen, dass Sie weniger strukturierte Werte erhalten, nach denen Sie mit Ihrer Anwendung suchen möchten, ist MySQL hier möglicherweise nicht die beste Wahl.

Wenn Sie PostgreSQL verwenden, können Sie möglicherweise das Beste aus beiden Welten herausholen. (Dies hängt wirklich von der tatsächlichen Struktur der Daten hier ab ... MySQL ist auch nicht unbedingt die falsche Wahl, und die NoSQL-Optionen können von Interesse sein. Ich schlage nur Alternativen vor.)

In der Tat kann PostgreSQL einen Index für (unveränderliche) Funktionen erstellen (was MySQL meines Wissens nicht kann), und in neueren Versionen können Sie PLV8 für die JSON-Daten direkt verwenden , um Indizes für bestimmte interessierende JSON-Elemente zu erstellen, was sich verbessern würde die Geschwindigkeit Ihrer Abfragen bei der Suche nach diesen Daten.

BEARBEITEN:

Ist es ratsam, beide Modelle zu verwenden, da es nicht zu viele Spalten gibt, in denen ich suchen muss? Schlüssel pro Spalte für die Daten, die ich suchen muss, und JSON für andere (in derselben MySQL-Datenbank)?

Das Mischen der beiden Modelle ist nicht unbedingt falsch (vorausgesetzt, der zusätzliche Speicherplatz ist vernachlässigbar), kann jedoch zu Problemen führen, wenn Sie nicht sicherstellen, dass die beiden Datensätze synchron bleiben: Ihre Anwendung darf niemals einen ändern, ohne auch den anderen zu aktualisieren .

Eine gute Möglichkeit, dies zu erreichen, besteht darin, dass ein Trigger die automatische Aktualisierung durchführt, indem er bei jeder Aktualisierung oder Einfügung eine gespeicherte Prozedur auf dem Datenbankserver ausführt. Soweit mir bekannt ist, wird die Sprache der gespeicherten MySQL-Prozeduren wahrscheinlich nicht für jede Art von JSON-Verarbeitung unterstützt. Auch hier sollte PostgreSQL mit PLV8-Unterstützung (und möglicherweise andere RDBMS mit flexibleren Sprachen für gespeicherte Prozeduren) nützlicher sein (das automatische Aktualisieren Ihrer relationalen Spalte mithilfe eines Triggers ähnelt dem Aktualisieren eines Index auf dieselbe Weise).

Bruno
quelle
Zusätzlich zu dem, was ich oben gesagt habe, kann es sich lohnen, sich die Operatoren für den JSONB-Datentyp in PostgreSQL 9.4 und höher anzusehen.
Bruno
1

Einige Zeit Joins auf dem Tisch sind ein Overhead. Sagen wir für OLAP. Wenn ich zwei Tabellen habe, ist eine ORDERS-Tabelle und eine ORDER_DETAILS. Um alle Bestelldetails zu erhalten, müssen wir zwei Tabellen verbinden. Dadurch wird die Abfrage langsamer, wenn keine der Zeilen in den Tabellen zunimmt, beispielsweise in Millionen. Die Links- / Rechts-Verknüpfung ist zu langsam als die innere Verknüpfung. Ich denke, wenn wir JSON-Zeichenfolge / Objekt in den jeweiligen ORDERS-Eintrag hinzufügen, wird JOIN vermieden. Das Hinzufügen von Berichten wird schneller sein ...

Ravindra
quelle
1

kurze Antwort Sie müssen zwischen ihnen mischen, verwenden Sie json für Daten, mit denen Sie keine Beziehung herstellen möchten, wie Kontaktdaten, Adresse, Produktvariablen

Ahmedfraije Aa
quelle
0

Sie versuchen, ein nicht relationales Modell in eine relationale Datenbank einzubauen . Ich denke, Sie sollten eine NoSQL-Datenbank wie MongoDB besser verwenden . Es gibt kein vordefiniertes Schema, das Ihrer Anforderung entspricht, die Anzahl der Felder nicht zu beschränken (siehe das typische Beispiel für eine MongoDB-Sammlung). Lesen Sie die MongoDB- Dokumentation , um eine Vorstellung davon zu erhalten, wie Sie Ihre Dokumente abfragen würden, z

db.mycollection.find(
    {
      name: 'sann'
    }
)
Chris L.
quelle
2
Aus Neugier haben Sie angenommen, dass sein Modell nicht relational ist. Die Informationen, die er oben angegeben hat, scheinen mir sehr relational zu sein.
Colin M
0

Wie andere bereits betont haben, werden Abfragen langsamer sein. Ich würde vorschlagen, stattdessen mindestens eine '_ID'-Spalte hinzuzufügen, um danach abzufragen.

Hose
quelle