Stocker JSON dans la base de données ou avoir une nouvelle colonne pour chaque clé

215

J'implémente le modèle suivant pour stocker des données liées à l'utilisateur dans ma table - j'ai 2 colonnes - uid(clé primaire) et une metacolonne qui stocke d'autres données sur l'utilisateur au format JSON.

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

Est - ce une meilleure façon (performance sage, sage-conception) que le modèle d' une colonne par propriété, où la table aura beaucoup de colonnes comme uid, name,emailid .

Ce que j'aime dans le premier modèle, c'est que vous pouvez ajouter autant de champs que possible, il n'y a pas de limitation.

Aussi, je me demandais, maintenant que j'ai implémenté le premier modèle. Comment puis-je effectuer une requête dessus, comme, je veux récupérer tous les utilisateurs qui ont un nom comme 'foo'?

Question - Quelle est la meilleure façon de stocker les données relatives aux utilisateurs (en gardant à l'esprit que le nombre de champs n'est pas fixe) dans la base de données en utilisant - JSON ou colonne par champ? De plus, si le premier modèle est implémenté, comment interroger la base de données comme décrit ci-dessus? Dois-je utiliser les deux modèles, en stockant toutes les données qui peuvent être recherchées par une requête dans une ligne distincte et les autres données en JSON (est une ligne différente)?


Mettre à jour

Puisqu'il n'y aura pas trop de colonnes sur lesquelles je dois effectuer une recherche, est-il sage d'utiliser les deux modèles? Clé par colonne pour les données que je dois rechercher et JSON pour les autres (dans la même base de données MySQL)?

ShuklaSannidhya
la source
40
bonne question! mais pourquoi n'avez-vous pas accepté une réponse? cela aiderait d'autres utilisateurs (comme moi)
Sahar Ch.

Réponses:

200

Mise à jour 4 juin 2017

Étant donné que cette question / réponse a gagné en popularité, j'ai pensé qu'il valait la peine d'être mis à jour.

Lorsque cette question a été initialement publiée, MySQL n'avait aucun support pour les types de données JSON et le support dans PostgreSQL était à ses balbutiements. Depuis la version 5.7, MySQL prend désormais en charge un type de données JSON (dans un format de stockage binaire) et PostgreSQL JSONB a mûri de manière significative. Les deux produits fournissent des types JSON performants qui peuvent stocker des documents arbitraires, notamment la prise en charge de l'indexation de clés spécifiques de l'objet JSON.

Cependant, je maintiens ma déclaration d'origine selon laquelle votre préférence par défaut, lors de l'utilisation d'une base de données relationnelle, devrait toujours être colonne par valeur. Les bases de données relationnelles reposent toujours sur l'hypothèse que les données qu'elles contiennent seront assez bien normalisées. Le planificateur de requêtes a de meilleures informations d'optimisation lors de la consultation des colonnes que lors de la consultation des clés d'un document JSON. Des clés étrangères peuvent être créées entre les colonnes (mais pas entre les clés dans les documents JSON). Surtout: si la majorité de votre schéma est suffisamment volatile pour justifier l'utilisation de JSON, vous voudrez peut-être au moins déterminer si une base de données relationnelle est le bon choix.

Cela dit, peu d'applications sont parfaitement relationnelles ou orientées document. La plupart des applications ont un mélange des deux. Voici quelques exemples où j'ai personnellement trouvé JSON utile dans une base de données relationnelle:

  • Lors du stockage des adresses e-mail et des numéros de téléphone d'un contact, où les stocker en tant que valeurs dans un tableau JSON est beaucoup plus facile à gérer que plusieurs tables distinctes

  • Enregistrement des préférences utilisateur de clé / valeur arbitraire (où la valeur peut être booléenne, textuelle ou numérique et où vous ne voulez pas avoir de colonnes distinctes pour différents types de données)

  • Stockage des données de configuration sans schéma défini (si vous construisez Zapier ou IFTTT et devez stocker des données de configuration pour chaque intégration)

Je suis sûr qu'il y en a d'autres aussi, mais ce ne sont que quelques exemples rapides.

Réponse originale

Si vous voulez vraiment pouvoir ajouter autant de champs que vous le souhaitez sans limitation (autre qu'une limite de taille de document arbitraire), envisagez une solution NoSQL telle que MongoDB.

Pour les bases de données relationnelles: utilisez une colonne par valeur. Mettre un blob JSON dans une colonne rend pratiquement impossible l'interrogation (et ralentit douloureusement lorsque vous trouvez réellement une requête qui fonctionne).

Les bases de données relationnelles tirent parti des types de données lors de l'indexation et sont destinées à être implémentées avec une structure normalisée .

En remarque: cela ne veut pas dire que vous ne devez jamais stocker JSON dans une base de données relationnelle. Si vous ajoutez de vraies métadonnées, ou si votre JSON décrit des informations qui n'ont pas besoin d'être interrogées et qui sont uniquement utilisées pour l'affichage, il peut être exagéré de créer une colonne distincte pour tous les points de données.

Colin M
la source
1
Puisqu'il n'y aura pas trop de colonnes sur lesquelles je dois effectuer une recherche, est-il sage d'utiliser les deux modèles? Clé par colonne pour les données que je dois rechercher et JSON pour les autres (dans la même base de données MySQL)?
ShuklaSannidhya
3
@Sann Vous devez utiliser une colonne par valeur pour les données que vous souhaitez lire ou interroger souvent. Mettre le nom de quelqu'un dans JSON n'a pas de sens car, même si vous n'êtes pas susceptible d'interroger sur cette base, vous en aurez probablement besoin très souvent. C'est beaucoup de décodage inutile de votre côté application. À moins que vous n'ayez vraiment l' impression que vos données sont mieux représentées en JSON (et croyez-moi, ce n'est probablement pas le cas), vous ne devriez pas y recourir.
Colin M
5
" virtually impossible to query" - aujourd'hui psql vous permet de rechercher et d'indexer son jsonb
ted
1
@ted vrai. Cependant, au moment de la rédaction de cette réponse, elle n'était pas vraiment disponible. En outre, cette question fait référence à MySQL dans lequel sa capacité n'est pas présente.
Colin M
3
@ColinM, oui, je réalise que mon commentaire a 3 ans de moins que votre message. La raison pour laquelle je l'ai quitté est parce que cela peut être utile et changer la décision pour les autres. Quant à la référence à MySQL: pourrait être vrai, mais ayez "For relational databases"dans votre réponse = P
ted
69

Comme la plupart des choses "ça dépend". Il n'est pas bon ou mauvais / bon ou mauvais en soi de stocker des données dans des colonnes ou JSON. Cela dépend de ce que vous devez en faire plus tard. Quelle est votre façon prédite d'accéder à ces données? Aurez-vous besoin de croiser d'autres données?

D'autres personnes ont très bien répondu à ce que sont les compromis techniques.

Peu de gens ont discuté de l'évolution de votre application et de ses fonctionnalités au fil du temps et de l'impact de cette décision de stockage de données sur votre équipe.

Parce que l'une des tentations d'utiliser JSON est d'éviter la migration du schéma et donc si l'équipe n'est pas disciplinée, il est très facile de coller encore une autre paire clé / valeur dans un champ JSON. Il n'y a pas de migration pour ça, personne ne se souvient à quoi ça sert. Il n'y a aucune validation dessus.

Mon équipe a utilisé JSON le long des colonnes traditionnelles dans les postgres et au début, c'était la meilleure chose depuis le pain tranché. JSON était attrayant et puissant, jusqu'au jour où nous avons réalisé que la flexibilité avait un prix et que c'est soudainement un vrai problème. Parfois, ce point monte très rapidement, puis il devient difficile de changer car nous avons construit tant d'autres choses en plus de cette décision de conception.

Les heures supplémentaires, l'ajout de nouvelles fonctionnalités, le fait d'avoir les données en JSON ont conduit à des requêtes plus compliquées que ce qui aurait pu être ajouté si nous nous en étions tenus aux colonnes traditionnelles. Nous avons donc commencé à pêcher certaines valeurs clés dans des colonnes afin de pouvoir faire des jointures et faire des comparaisons entre les valeurs. Mauvaise idée. Maintenant, il y avait duplication. Un nouveau développeur viendrait à bord et serait confus? Quelle est la valeur dans laquelle je devrais sauvegarder? Le JSON ou la colonne?

Les champs JSON sont devenus des tiroirs indésirables pour de petits morceaux de ceci et cela. Aucune validation des données au niveau de la base de données, aucune cohérence ou intégrité entre les documents. Cela a poussé toute cette responsabilité dans l'application au lieu d'obtenir une vérification du type et des contraintes à partir des colonnes traditionnelles.

Avec le recul, JSON nous a permis d'itérer très rapidement et de sortir quelque chose. C'était super. Cependant, après avoir atteint une certaine taille d'équipe, sa flexibilité nous a également permis de nous accrocher à une longue corde de dette technique qui a ensuite ralenti la progression de l'évolution des fonctionnalités. Utiliser avec précaution.

Réfléchissez longuement à la nature de vos données. C'est le fondement de votre application. Comment les données seront-elles utilisées au fil du temps. Et comment est-il susceptible de changer?

Homan
la source
7
"sa flexibilité nous a aussi permis de nous accrocher à une longue corde de dette technique" très belle métaphore!
Antoine Gallix
Après de nombreuses années de développement et de travail avec différentes personnes, si j'écris sur ce sujet, j'écrirai la même chose. Il y a tellement de développeurs maintenant, où beaucoup d'entre eux, même avec des années d'expérience, ne progressent pas réellement. Nous devons garder tout simple et pour moi les 2 choses que nous devons toujours considérer qui peuvent "encadrer" le succès sont l'évolutivité et la maintenabilité du code.
JohnnyJaxs
27

Il suffit de le jeter là-bas, mais WordPress a une structure pour ce genre de choses (au moins WordPress a été le premier endroit où je l'ai observé, il est probablement né ailleurs).

Il permet des clés illimitées et est plus rapide à rechercher qu'à l'aide d'un blob JSON, mais pas aussi rapidement que certaines des solutions NoSQL.

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

ÉDITER

Pour stocker l'historique / plusieurs clés

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
.................

et interrogez via quelque chose comme ceci:

select meta_val from `table` where meta_key = 'name' and uid = 1 order by meta_id desc
Adam
la source
1
Je serais curieux de voir si une solution NoSQL fonctionne vraiment mieux qu'une requête relationnelle sur une clé d'index correctement. Je soupçonne que cela devrait être plus ou moins le même sur un exemple à 1 niveau comme celui-ci.
Bruno
+1. Je l'ai aussi remarqué! Mais cela vous donne une énorme table (en termes de lignes). De plus, vous ne pouvez pas stocker plusieurs valeurs, par exemple, si l'utilisateur change son nom, mais je veux également conserver l'ancien nom, dans ce cas, j'aurai besoin d'un modèle de données de type JSON.
ShuklaSannidhya
@Sann, si vous souhaitez conserver l'ancienne valeur en JSON, vous devrez également renommer la clé: vous pouvez le faire avec un EAV (qui est ce que cet exemple est) ou JSON. Ce n'est pas particulièrement différent.
Bruno
Cela vous donne une énorme table, mais comme pour les valeurs en double, vous rencontrez le même problème avec JSON - vous ne pouvez pas avoir de clés en double au même niveau (par exemple, deux clés "nom") et vous attendre à un comportement prévisible.
Adam
Bien sûr, vous ne pouvez pas avoir de clés en double, mais un tableau peut être associé à cette clé. Découvrez la emailidclé dans l'exemple que j'ai donné dans ma question.
ShuklaSannidhya
13

l'inconvénient de l'approche est exactement ce que vous avez mentionné:

cela rend TRÈS lent à trouver des choses, car à chaque fois vous devez effectuer une recherche de texte dessus.

La valeur par colonne correspond à la place à la chaîne entière.

Votre approche (données basées sur JSON) convient parfaitement aux données que vous n'avez pas besoin de rechercher, et que vous n'avez qu'à afficher avec vos données normales.

Edit: Juste pour clarifier, ce qui précède vaut pour les bases de données relationnelles classiques. NoSQL utilise JSON en interne et est probablement une meilleure option si tel est le comportement souhaité.

Nick Andriopoulos
la source
1
Donc tu veux dire, je devrais utiliser les deux. Clé par colonne pour les données que je dois rechercher et JSON pour les autres, non?
ShuklaSannidhya
4
Oui. de cette façon, vous obtenez les performances requises en recherchant les champs de données par colonne et en récupérant le blob JSON à utiliser dans le code si nécessaire.
Nick Andriopoulos
9

Fondamentalement, le premier modèle que vous utilisez est appelé stockage basé sur des documents. Vous devriez jeter un œil à la base de données documentaire NoSQL populaire comme MongoDB et CouchDB . Fondamentalement, dans les bases de données basées sur des documents, vous stockez des données dans des fichiers json, puis vous pouvez interroger ces fichiers json.

Le deuxième modèle est la structure de base de données relationnelle populaire.

Si vous souhaitez utiliser une base de données relationnelle comme MySql, je vous suggère de n'utiliser que le deuxième modèle. Il est inutile d'utiliser MySql et de stocker des données comme dans le premier modèle .

Pour répondre à votre deuxième question, il n'y a aucun moyen d'interroger le nom comme 'foo' si vous utilisez le premier modèle .

Girish
la source
Est-il sage d'utiliser les deux modèles? Clé par colonne pour les données que je dois rechercher et JSON pour les autres (dans la même base de données)?
ShuklaSannidhya
@Sann - haha. C'est la duplication des données. Vous devrez vous assurer que les deux éléments de données sont toujours les mêmes. Même si l'une des données est différente à tout moment, vos données ne sont pas propres et peuvent entraîner de graves problèmes. Donc, ma réponse est NON
Girish
Mais la redondance n'est pas coûteuse lorsque les données redondantes sont petites, disons qu'il n'y a que deux champs sur lesquels j'ai besoin d'effectuer une recherche, donc je crée deux nouvelles colonnes pour eux, [peut-être] les supprimer de mes données JSON [/ peut-être] . Ce ne sera pas une duplication coûteuse, non?
ShuklaSannidhya
Si vous regardez les performances, MongoDB et CouchDB fournissent des opérations de lecture et d'écriture plus rapides que MySql car elles n'offrent pas beaucoup de fonctionnalités dans les bases de données relationnelles qui ne sont pas requises dans la plupart des cas d'utilisation.
Girish
L'avantage ne serait-il pas de stocker des objets / rappels JSON à partir d'une API? Par exemple, au lieu d'appeler l'API de YouTube pour URL, pouce, etc., vous pouvez simplement interroger votre base de données locale (mysql, lite, etc.) pour l'objet JSON? Je ne sais pas, cela a du sens pour moi, surtout si vous essayez de mettre en cache ou de faire fonctionner une application plus rapidement. Mais je ne suis pas un professionnel: /
markbratanov
4

Il semble que vous hésitiez principalement à utiliser ou non un modèle relationnel.

Dans l'état actuel des choses, votre exemple correspondrait assez bien à un modèle relationnel, mais le problème peut bien sûr survenir lorsque vous devez faire évoluer ce modèle.

Si vous n'avez qu'un (ou quelques niveaux prédéterminés) d'attributs pour votre entité principale (utilisateur), vous pouvez toujours utiliser un modèle de valeur d'attribut d'entité (EAV) dans une base de données relationnelle. (Cela a aussi ses avantages et ses inconvénients.)

Si vous prévoyez d'obtenir des valeurs moins structurées que vous voudrez rechercher à l'aide de votre application, MySQL n'est peut-être pas le meilleur choix ici.

Si vous utilisiez PostgreSQL, vous pourriez potentiellement tirer le meilleur parti des deux mondes. (Cela dépend vraiment de la structure réelle des données ici ... MySQL n'est pas nécessairement le mauvais choix non plus, et les options NoSQL peuvent être intéressantes, je suggère simplement des alternatives.)

En effet, PostgreSQL peut construire un index sur des fonctions (immuables) (ce que MySQL ne peut pas pour autant que je sache) et dans les versions récentes, vous pourriez utiliser PLV8 sur les données JSON directement pour construire des index sur des éléments JSON spécifiques d'intérêt, ce qui améliorerait la vitesse de vos requêtes lors de la recherche de ces données.

ÉDITER:

Puisqu'il n'y aura pas trop de colonnes sur lesquelles je dois effectuer une recherche, est-il sage d'utiliser les deux modèles? Clé par colonne pour les données que je dois rechercher et JSON pour les autres (dans la même base de données MySQL)?

Mélanger les deux modèles n'est pas nécessairement faux (en supposant que l'espace supplémentaire est négligeable), mais cela peut causer des problèmes si vous ne vous assurez pas que les deux ensembles de données sont synchronisés: votre application ne doit jamais changer l'un sans mettre à jour l'autre également .

Un bon moyen d'y parvenir serait de demander à un déclencheur d'effectuer la mise à jour automatique, en exécutant une procédure stockée dans le serveur de base de données chaque fois qu'une mise à jour ou une insertion est effectuée. Pour autant que je sache, le langage de procédure stockée MySQL manque probablement de support pour tout type de traitement JSON. Encore une fois, PostgreSQL avec prise en charge PLV8 (et éventuellement d'autres SGBDR avec des langages de procédures stockées plus flexibles) devrait être plus utile (la mise à jour automatique de votre colonne relationnelle à l'aide d'un déclencheur est assez similaire à la mise à jour d'un index de la même manière).

Bruno
la source
En plus de ce que j'ai dit ci-dessus, il peut être intéressant de regarder les opérateurs pour le type de données JSONB dans PostgreSQL 9.4 et supérieur.
Bruno
1

un certain temps, les jointures sur la table seront une surcharge. disons pour OLAP. si j'ai deux tables, l'une est la table ORDERS et l'autre est ORDER_DETAILS. Pour obtenir tous les détails de la commande, nous devons joindre deux tables, cela rendra la requête plus lente lorsqu'aucune ligne des tables n'augmentera, disons en millions ou plus. La jointure gauche / droite est trop lente que la jointure interne. Je pense que si nous ajoutons une chaîne / un objet JSON dans l'entrée ORDERS respective, JOIN sera évité. ajouter la génération de rapports sera plus rapide ...

Ravindra
la source
1

réponse courte vous devez mélanger entre eux, utilisez json pour les données que vous n'allez pas faire de relations avec eux comme les coordonnées, l'adresse, les produits variabls

Ahmedfraije Aa
la source
0

Vous essayez d'intégrer un modèle non relationnel dans une base de données relationnelle, je pense que vous seriez mieux servi en utilisant une base de données NoSQL telle que MongoDB . Il n'y a pas de schéma prédéfini qui correspond à votre exigence de ne pas avoir de limitation au nombre de champs (voir l'exemple de collection MongoDB typique). Consultez la documentation MongoDB pour avoir une idée de la façon dont vous interrogeriez vos documents, par exemple

db.mycollection.find(
    {
      name: 'sann'
    }
)
Chris L
la source
2
Par curiosité, ce qui vous a fait supposer que son modèle n'est pas relationnel. Les informations qu'il a mises ci-dessus me semblent très relationnelles.
Colin M
0

Comme d'autres l'ont souligné, les requêtes seront plus lentes. Je suggérerais d'ajouter au moins une colonne '_ID' pour interroger à la place.

Un pantalon
la source