Qu'est-ce qui est le plus efficace: plusieurs tables MySQL ou une grande table?

103

Je stocke divers détails utilisateur dans ma base de données MySQL. À l'origine, il a été mis en place dans diverses tables, ce qui signifie que les données sont liées aux UserIds et sont sorties via des appels parfois compliqués pour afficher et manipuler les données selon les besoins. Lors de la mise en place d'un nouveau système, il est presque logique de combiner toutes ces tables en une seule grande table de contenu connexe.

  • Cela va-t-il être une aide ou un obstacle?
  • Considérations de vitesse lors de l'appel, de la mise à jour ou de la recherche / manipulation?

Voici un exemple de certaines de mes structures de table:

  • utilisateurs - UserId, nom d'utilisateur, email, mot de passe crypté, date d'enregistrement, ip
  • user_details - données de cookie, nom, adresse, coordonnées, affiliation, données démographiques
  • user_activity - contributions, dernière connexion, dernière consultation
  • user_settings - paramètres d'affichage du profil
  • user_interests - variables ciblables publicitaires
  • user_levels - droits d'accès
  • user_stats - hits, comptes

Edit: J'ai voté pour toutes les réponses jusqu'à présent, elles ont toutes des éléments qui répondent essentiellement à ma question.

La plupart des tables ont une relation 1: 1 qui était la principale raison de leur dénormalisation.

Y aura-t-il des problèmes si le tableau s'étend sur plus de 100 colonnes alors qu'une grande partie de ces cellules est susceptible de rester vide?

Peter Craig
la source
Cette autre question pourrait être utile aussi
Mosty Mostacho

Réponses:

66

Plusieurs tableaux aident des manières / cas suivants:

(a) si différentes personnes vont développer des applications impliquant différentes tables, il est logique de les diviser.

(b) Si vous souhaitez attribuer différents types d'autorités à différentes personnes pour différentes parties de la collecte de données, il peut être plus pratique de les répartir. (Bien sûr, vous pouvez envisager de définir des vues et de les autoriser de manière appropriée).

(c) Pour déplacer des données vers différents endroits, en particulier pendant le développement, il peut être judicieux d'utiliser des tables résultant en des fichiers de plus petite taille.

(d) Une empreinte plus petite peut être plus confortable pendant que vous développez des applications sur la collecte de données spécifique d'une seule entité.

(e) C'est une possibilité: ce que vous pensiez comme une donnée à valeur unique peut s'avérer être en réalité plusieurs valeurs à l'avenir. Par exemple, la limite de crédit est un champ à valeur unique à partir de maintenant. Mais demain, vous pouvez décider de changer les valeurs comme (date du, date au, valeur du crédit). Les tables fractionnées pourraient être utiles maintenant.

Mon vote serait pour plusieurs tables - avec des données convenablement réparties.

Bonne chance.

user115905
la source
3
@RohitKhatri: Au meilleur de ma connaissance, avoir plusieurs tables augmentera les performances dans la plupart des cas.
Hari Harker
1
@HariHarker Merci pour votre réponse, mais j'ai compris que cela dépend de votre modèle d'accès.
Rohit Khatri
Jusqu'à récemment, je stockais toujours toutes les données dans une table, mais à bien y penser, il présente de nombreux avantages de fractionner les données en termes de performances (selon le cas d'utilisation bien sûr), de sémantique (certaines données sont mieux regroupées dans un tableau différent) et développement. Par exemple, je développe actuellement un système ERP personnalisé au-dessus d'un système hérité. J'ai dû étendre les anciennes tables de base de données avec des colonnes supplémentaires. J'ai décidé de créer de nouvelles tables pour les nouvelles données. Certaines nouvelles fonctionnalités sont utiles pour l'ancien système et maintenant je peux facilement les intégrer sans avoir à réécrire trop d'anciennes requêtes
Ogier Schelvis
35

La combinaison des tables s'appelle la dénormalisation.

Cela peut (ou non) aider à JOINaccélérer certaines requêtes (qui font beaucoup de s) au détriment de la création d'un enfer de maintenance.

MySQLest capable d'utiliser uniquement la JOINméthode, à savoir NESTED LOOPS.

Cela signifie que pour chaque enregistrement de la table MySQLpilotée , localise un enregistrement correspondant dans la table pilotée dans une boucle.

La localisation d'un enregistrement est une opération assez coûteuse qui peut prendre des dizaines de fois plus longtemps que la simple numérisation de l'enregistrement.

Le fait de déplacer tous vos enregistrements dans une table vous aidera à vous débarrasser de cette opération, mais la table elle-même s'agrandit et l'analyse de la table prend plus de temps.

Si vous avez beaucoup d'enregistrements dans d'autres tables, l'augmentation de l'analyse de table peut surpondérer les avantages des enregistrements analysés de manière séquentielle.

L'enfer d'entretien, en revanche, est garanti.

Quassnoi
la source
1
Si vous avez 10000 utilisateurs et que vous effectuez correctement une jointure avec une base de données configurée avec des clés étrangères, vous ne devriez avoir besoin que de la recherche intense en faisant quelque chose comme select * from users où name = "bob". Une fois que vous avez bob, vous utilisez un index pour trouver les tables jointes à bobiner, ce qui est beaucoup plus rapide car vous utilisez l'identifiant de bob. Cela se produit indépendamment du fait que vous effectuiez une jointure dans votre requête ou que vous interrogiez Bob, puis que vous interrogiez une table séparément. Bien sûr, j'espère que votre deuxième requête est basée sur l'identifiant de bob et non sur autre chose.
Rudy Garcia le
17

Sont-ils tous des relations 1: 1? Je veux dire, si un utilisateur pouvait appartenir, disons, à différents niveaux d'utilisateurs, ou si les intérêts des utilisateurs sont représentés sous forme de plusieurs enregistrements dans la table des intérêts des utilisateurs, alors fusionner ces tables serait immédiatement hors de question.

En ce qui concerne les réponses précédentes sur la normalisation, il faut dire que les règles de normalisation de la base de données ont complètement ignoré les performances et ne regardent que ce qu'est une conception de base de données soignée. C'est souvent ce que vous voulez réaliser, mais il y a des moments où il est logique de se dénormaliser activement à la recherche de la performance.

Dans l'ensemble, je dirais que la question se résume à combien de champs il y a dans les tableaux et à quelle fréquence ils sont consultés. Si l'activité de l'utilisateur n'est souvent pas très intéressante, il peut être gênant de toujours l'avoir sur le même enregistrement, pour des raisons de performances et de maintenance. Si certaines données, comme les paramètres, par exemple, sont consultées très souvent, mais contiennent simplement trop de champs, il peut également ne pas être pratique de fusionner les tables. Si vous n'êtes intéressé que par le gain de performances, vous pouvez envisager d'autres approches, telles que garder les paramètres séparés, mais les enregistrer dans une variable de session qui leur est propre afin de ne pas avoir à interroger la base de données très souvent.

David Hedlund
la source
Je suis complètement en désaccord avec votre commentaire selon lequel la normalisation se concentre uniquement sur la netteté et ne tient absolument pas compte des performances. Il y a un compromis dans les deux scénarios et la dénormalisation met en fait l'intégrité des données en danger. Je dirais que la normalisation de votre base de données améliore en fait les performances globales de la base de données plutôt que d'avoir une augmentation de performances négligeable rapide à partir d'une table dénormalisée.
Rudy Garcia le
Étant donné que la discussion porte spécifiquement sur les relations 1: 1, le fractionnement des tables n'est pas une tâche de normalisation , non? S'il n'y a aucune information dupliquée, c'est normal même s'il s'agit d'une seule table. (Eh bien, cela pourrait ne pas satisfaire la 3NFnormalisation, alors profitez d'une deuxième table pour résoudre cela, mais cela ne semble pas être ce à quoi OP fait référence pour les autres tables.)
ToolmakerSteve
14

Faites toutes ces tables ont une 1-to-1relation? Par exemple, chaque ligne utilisateur aura-t-elle une seule ligne correspondante dans user_statsou user_levels? Si tel est le cas, il peut être judicieux de les combiner en un seul tableau. Si la relation ne l'est pas 1 to 1 , il n'aurait probablement pas de sens de les combiner (dénormaliser).

Les avoir dans des tables séparées par rapport à une table aura probablement peu d'effet sur les performances, sauf si vous avez des centaines de milliers ou des millions d'enregistrements utilisateur. Le seul réel gain que vous obtiendrez est de simplifier vos requêtes en les combinant.

ETA:

Si vous craignez d'avoir trop de colonnes , pensez à ce que vous utilisez généralement ensemble et combinez-les , en laissant le reste dans une table séparée (ou plusieurs tables séparées si nécessaire).

Si vous regardez la façon dont vous utilisez les données, je suppose que vous constaterez que quelque chose comme 80% de vos requêtes utilisent 20% de ces données, les 80% restants n'étant utilisés qu'occasionnellement. Combinez ces 20% fréquemment utilisés dans une table et laissez les 80% que vous n'utilisez pas souvent dans des tables séparées et vous aurez probablement un bon compromis.

Eric Petroelje
la source
Oui, chaque table n'a qu'une seule ligne pour chaque utilisateur, simplement pour éviter le mal de tête lié à la gestion d'un grand nombre de données dupliquées. C'est pourquoi je pense qu'une table convient. Si les données utilisateur couvraient plusieurs lignes, je m'attendrais à ce que ces tables soient séparées de la table utilisateur principale.
Peter Craig
1
Si chaque table a une relation 1 à 1, une table serait plus facile à utiliser. Il n'est pas nécessaire de diviser la table dans ce cas. Le fractionnement du tableau suggère qu'il y a plus d'une ligne, ce qui pourrait conduire à un cas où un autre développeur les traiterait de cette façon.
Richard L
Pensée très intéressante appliquant 80/20 à la conception de table de base de données. Cela m'a amené à réfléchir également à la conception de classe OOP (je suis principalement un développeur Java) et à me demander si la même chose pourrait être efficace là-bas (mettre la fonctionnalité d'application principale à 80% dans une classe et le reste dans d'autres classes).
Zack Macomber
1
@ZackMacomber - Non, le fractionnement de classe doit être basé sur la localité de référence . L'avantage de la division en plusieurs classes est de dessiner une bordure autour d'une plus petite unité de fonctionnalité, de sorte qu'il soit plus facile de comprendre / tester / modifier, et de clairement où cette unité interagit avec d'autres unités de fonctionnalité. L'objectif est de conserver la plupart des connexions (références, appels) à l' intérieur d' une seule unité, avec peu de connexions entre les unités . La définition de plusieurs interfaces que la classe implémente, avec une interface différente par cas d'utilisation, peut être une première étape utile vers ce fractionnement.
ToolmakerSteve
@ToolmakerSteve Bonnes pensées +1
Zack Macomber
9

La création d'une table massive va à l'encontre des principaux de la base de données relationnelle. Je ne les combinerais pas tous en une seule table. Vous allez obtenir plusieurs instances de données répétées. Si votre utilisateur a trois intérêts par exemple, vous aurez 3 lignes, avec les mêmes données utilisateur juste pour stocker les trois intérêts différents. Optez définitivement pour l'approche de table multiple «normalisée». Consultez cette page Wiki pour la normalisation de la base de données.

Edit: J'ai mis à jour ma réponse, car vous avez mis à jour votre question ... Je suis encore plus d'accord avec ma réponse initiale depuis ...

une grande partie de ces cellules restera probablement vide

Si, par exemple, un utilisateur n'avait aucun intérêt, si vous normalisez, vous n'aurez simplement pas de ligne dans la table des intérêts pour cet utilisateur. Si vous avez tout dans une table massive, alors vous aurez des colonnes (et apparemment beaucoup d'entre elles) qui ne contiennent que des NULL.

J'ai travaillé pour une entreprise de téléphonie où il y avait des tonnes de tables, l'obtention de données pouvait nécessiter de nombreuses jointures. Lorsque les performances de lecture de ces tables étaient critiques, des procédures ont été créées pour générer une table plate (c'est-à-dire une table dénormalisée) qui ne nécessiterait aucune jointure, aucun calcul, etc. vers lequel les rapports pourraient pointer. Ceux-ci étaient ensuite utilisés en conjonction avec un agent serveur SQL pour exécuter le travail à certains intervalles (c'est-à-dire qu'une vue hebdomadaire de certaines statistiques s'exécuterait une fois par semaine et ainsi de suite).


la source
J'aime cette approche, car les données dénormalisées n'existent que temporairement, comme un instantané d'un moment dans le temps. Aucun problème d'insertion / modification / suppression - jetez-le simplement une fois terminé.
ToolmakerSteve
7

Pourquoi ne pas utiliser la même approche que Wordpress en ayant une table des utilisateurs avec des informations utilisateur de base que tout le monde possède, puis en ajoutant une table "user_meta" qui peut essentiellement être n'importe quelle paire clé / valeur associée à l'ID utilisateur. Donc, si vous avez besoin de trouver toutes les méta-informations pour l'utilisateur, vous pouvez simplement les ajouter à votre requête. Vous ne devrez pas non plus toujours ajouter la requête supplémentaire si cela n'est pas nécessaire pour des choses comme la connexion. L'avantage de cette approche laisse également votre table ouverte à l'ajout de nouvelles fonctionnalités à vos utilisateurs, telles que le stockage de leur identifiant Twitter ou de chaque intérêt individuel. Vous n'aurez pas non plus à gérer un labyrinthe d'identifiants associés, car vous avez une table qui régit toutes les métadonnées et vous la limiterez à une seule association au lieu de 50.

Wordpress le fait spécifiquement pour permettre l'ajout de fonctionnalités via des plugins, permettant ainsi à votre projet d'être plus évolutif et ne nécessitera pas une refonte complète de la base de données si vous devez ajouter une nouvelle fonctionnalité.

Rudy Garcia
la source
Le wp_usermetatableau Wordpress se développe géométriquement. Chaque utilisateur ajoute X lignes à la wp_usermetatable, une ligne pour chaque méta-information que nous voulons conserver pour cet utilisateur. Si vous conservez 8 champs personnalisés pour chaque utilisateur, cela signifie que wp_usermeta comportera des users * 8lignes. Cela semble causer des problèmes de performances, mais je ne sais pas si c'est le problème ou non…
tiers le
1
Je pourrais voir comment cela pourrait causer des problèmes de performances si vous avez des dizaines de milliers d'utilisateurs. Fondamentalement, la base de données devrait rechercher parmi 10000 * 8 entrées dans la méta-table utilisateur pour trouver celles que vous recherchez. Cependant, si vous n'interrogez les métadonnées qu'en cas de besoin, je pense que vos performances seraient meilleures. Si vous demandez toujours les métadonnées même lorsque vous n'en avez pas besoin, vous pourriez avoir des problèmes. Si vous avez toujours besoin des métadonnées, peut-être que le fractionnement des tables n'est pas la meilleure approche.
Rudy Garcia
1
Hier encore, nous avons traité d'un thème WP qui chargeait tous les utilisateurs (en utilisant get_users()) juste pour calculer la pagination. Une fois que nous avons corrigé le code pour utiliser une SELECT COUNT(…)requête pour la pagination à la place, le temps de chargement de la page est passé de 28 secondes à environ 400 ms. Je me demande toujours comment les performances se comparent aux tables jointes ou à une seule table plate… J'ai eu du mal à trouver des mesures de performances sur le Web.
troisième
En pensant à mon commentaire précédent, il semblerait que le fractionnement du tableau soit toujours efficace à moins que pour une raison quelconque, comme l'exemple de pagination ci-dessus, vous deviez sélectionner tous les utilisateurs. Bien que si vous récupérez toutes les méta-informations, vous aurez toujours 80 000 entrées dans la table usermeta. C'est beaucoup de recherches. Peut-être que quelqu'un pourrait tester quelle est la meilleure approche en exécutant un script sur les deux implémentations et l'exécuter 100 fois pour obtenir la moyenne, je pourrais peut-être le faire.
Rudy Garcia
1
Je l'ai relu aujourd'hui et j'ai réalisé que mon commentaire sur 10000 * 8 entrées est vrai, mais la façon dont fonctionne une base de données devrait en faire un problème. Si pour une raison quelconque vous saisissiez les 10000 utilisateurs ET leurs méta-informations, ce serait ridicule. Je ne peux penser à aucun scénario où vous voudriez cela. Une base de données récupérera facilement la méta pour un seul utilisateur à une vitesse fulgurante grâce aux clés étrangères et à l'indexation. En supposant que votre modèle de base de données est configuré correctement.
Rudy Garcia le
5

Je pense que c'est une de ces situations «ça dépend». Avoir plusieurs tables est plus propre et probablement mieux en théorie. Mais lorsque vous devez joindre 6 à 7 tables pour obtenir des informations sur un seul utilisateur, vous pouvez commencer à repenser cette approche.

Tundey
la source
1

Je dirais que cela dépend de ce que signifient vraiment les autres tableaux. Un user_details contient-il plus de 1 de plus / users et ainsi de suite. Le niveau de normalisation le mieux adapté à vos besoins dépend de vos demandes.

Si vous avez une table avec un bon index, ce serait probablement plus rapide. Mais d'un autre côté probablement plus difficile à maintenir.

Pour moi, il semble que vous pourriez ignorer User_Details car il s'agit probablement d'une relation 1 à 1 avec les utilisateurs. Mais le reste est probablement beaucoup de lignes par utilisateur?

Richard L
la source