Question

Pendant mon temps libre, j'ai commencé à écrire un petit jeu multijoueur avec une base de données. Je cherchais à séparer les informations de connexion des joueurs des autres informations de jeu (inventaire, statistiques et statut) et un ami en a parlé, ce n’était peut-être pas la meilleure idée.

Serait-il préférable de tout mettre dans une même table?

Était-ce utile?

La solution

Commencez par ignorer les performances et créez simplement la conception de base de données la plus logique et la plus compréhensible possible. Si les joueurs et les objets de jeu (épées? Pièces d'échecs?) Sont des objets distincts sur le plan conceptuel, placez-les dans des tableaux distincts. Si un joueur peut transporter des objets, vous mettez une clé étrangère dans le champ "objets". tableau qui fait référence aux "joueurs" table. Et ainsi de suite.

Ensuite, lorsque vous avez des centaines de joueurs et des milliers de choses, et que les joueurs courent dans le jeu et font des choses qui nécessitent des recherches dans la base de données, votre conception sera encore encore assez rapide, si vous venez d'ajouter les index appropriés.

Bien sûr, si vous planifiez des milliers de lecteurs simultanés, chacun inventoriant ses objets à la seconde, ou peut-être une autre charge énorme de recherches dans la base de données (une recherche pour chaque image rendue par le moteur graphique?), vous aurez besoin de penser à la performance. Mais dans ce cas, une base de données relationnelle typique basée sur disque ne sera de toute façon pas assez rapide, quelle que soit la conception de vos tables.

Autres conseils

Les bases de données relationnelles fonctionnent sur des ensembles. Une requête de base de données est là pour ne fournir aucun résultat ("introuvable") ou plusieurs résultats. Une base de données relationnelle n’est pas destinée à fournir toujours exactement un résultat en exécutant une requête (sur des relations).

A déclaré que je voulais mentionner qu'il y a aussi la vraie vie ;-). Une relation un à un pourrait avoir un sens, c'est-à-dire que si le nombre de colonnes de la table atteint un niveau critique, il pourrait être plus rapide de scinder cette table. Mais ce genre de conditions sont vraiment rares.

Si vous n'avez pas vraiment besoin de diviser la table, ne le faites pas. Au moins, je suppose que dans votre cas, vous devez sélectionner plus de deux tables pour obtenir toutes les données. C'est probablement plus lent que de tout stocker dans une seule table. Et votre logique de programmation deviendra au moins un peu moins lisible en le faisant.

Edith dit : à propos de la normalisation: Eh bien, je n'ai jamais vu une base de données normalisée à son maximum et personne ne le recommande vraiment comme option. Quoi qu’il en soit, si vous ne savez pas exactement si des colonnes seront normalisées (c’est-à-dire placées dans d’autres tables) ultérieurement, il est également plus facile de le faire avec une seule table au lieu de "table" un à un - " autre table "

S'il faut garder

  

informations de connexion du lecteur [séparées] des autres dans   informations de jeu (inventaire, statistiques,   et statut)

est souvent une question de normalisation. Vous voudrez peut-être jeter un coup d’œil sur le wikipedia ( Troisième forme normale , Dénormalisation ). Que vous les sépariez en plusieurs tables dépend des données stockées. En développant votre question, cela sera concret. Les données que nous souhaitons stocker sont:

  • nom d'utilisateur: nom unique permettant à un utilisateur de se connecter au système
  • passwd: mot de passe: associé au nom d'utilisateur garantissant que l'utilisateur est unique
  • email: adresse email de l'utilisateur; de sorte que le jeu peut "poke" l'utilisateur lorsqu'il n'a pas joué récemment
  • caractère: éventuellement nom de jeu distinct pour le caractère
  • statut: est le joueur et / ou le personnage actuellement actif
  • repères: un exemple de statistique pour le caractère

Nous pourrions stocker ceci sous forme de table unique ou de plusieurs tables.

Exemple de table unique

Si les joueurs ont un seul personnage dans ce monde de jeu, une seule table peut convenir. Par exemple,

CREATE TABLE players(
  id         INTEGER NOT NULL,
  username   VARCHAR2(32) NOT NULL,
  password   VARCHAR2(32) NOT NULL,
  email      VARCHAR2(160),
  character  VARCHAR2(32) NOT NULL,
  status     VARCHAR2(32),
  hitpoints  INTEGER,

  CONSTRAINT pk_players PRIMARY KEY (uid)
);

Cela vous permettrait de trouver toutes les informations pertinentes sur un joueur avec une seule requête sur la table des joueurs.

Exemple de table multiple

Toutefois, si vous souhaitez autoriser un seul joueur à avoir plusieurs personnages différents, vous souhaitez partager ces données sur deux tables différentes. Par exemple, vous pouvez effectuer les opérations suivantes:

-- track information on the player
CREATE TABLE players(
  id         INTEGER NOT NULL,
  username   VARCHAR2(32) NOT NULL,
  password   VARCHAR2(32) NOT NULL,
  email      VARCHAR2(160),

  CONSTRAINT pk_players PRIMARY KEY (id)
);

-- track information on the character
CREATE TABLE characters(
  id         INTEGER NOT NULL,
  player_id  INTEGER NOT NULL,
  character  VARCHAR2(32) NOT NULL,
  status     VARCHAR2(32),
  hitpoints  INTEGER,

  CONSTRAINT pk_characters PRIMARY KEY (id)
);
-- foreign key reference
ALTER TABLE characters
  ADD CONSTRAINT characters__players
  FOREIGN KEY (player_id) REFERENCES players (id);

Ce format nécessite des jointures lorsqu’on examine les informations relatives au joueur et au personnage; toutefois, la mise à jour des enregistrements risque moins d'entraîner des incohérences. Ce dernier argument est généralement utilisé lorsque vous préconisez plusieurs tables. Par exemple, si vous avez un lecteur (LotRFan) avec deux caractères (gollum, frodo) au format tableau unique, vous devez dupliquer le nom d'utilisateur, le mot de passe et les champs de courrier électronique. Si le joueur voulait changer son email / mot de passe, vous devrez modifier les deux enregistrements. Si un seul enregistrement a été modifié, la table devient incohérente. Toutefois, si LotRFan souhaite modifier son mot de passe dans le format de tableau multiple, une seule ligne du tableau est mise à jour.

Autres considérations

Le choix d'utiliser une ou plusieurs tables dépend des données sous-jacentes. Ce qui n’a pas été décrit ici mais qui a été noté dans d’autres réponses, c’est que les optimisations prennent souvent deux tableaux et les combinent en un seul.

Il est également intéressant de connaître les types de modifications qui seront apportées. Par exemple, si vous deviez choisir d'utiliser une seule table pour les joueurs pouvant avoir plusieurs personnages et voulant garder une trace du nombre total de commandes émises par le joueur en incrémentant un champ dans la table des joueurs; cela entraînerait la mise à jour de davantage de lignes dans l'exemple de table unique.

De toute façon, l’inventaire est une relation plusieurs-à-un, il mérite donc probablement sa propre table (indexée au moins sur sa clé étrangère).

Vous pouvez également souhaiter que les éléments personnels de chaque utilisateur soient distincts des éléments publics (c'est-à-dire ce que les autres peuvent voir de vous). Cela peut fournir de meilleurs résultats en mémoire cache, car de nombreuses personnes verront vos attributs publics, mais vous seul verrez vos attributs personnels.

Une relation 1-1 est juste une division verticale. Rien d'autre. faites-le si, pour une raison quelconque, vous ne stockez pas toutes les données dans la même table (par exemple, pour partitionner plusieurs serveurs, etc.)

Comme vous pouvez le constater, le consensus général est que "cela dépend". L’optimisation des performances / requêtes nous vient facilement à l’esprit - comme mentionné par @Campbell et d’autres.

Mais je pense que pour le type de base de données spécifique à une application que vous construisez, il est préférable de commencer par examiner la conception globale de l'application. Pour les bases de données spécifiques à une application (par opposition aux bases de données conçues pour être utilisées avec de nombreuses applications ou outils de reporting), le modèle de données "idéal" est probablement celui qui correspond le mieux à votre modèle de domaine tel qu'il est implémenté dans le code de votre application.

Vous ne pouvez pas appeler votre conception MVC . savoir quelle langue vous utilisez, mais je suppose que vous avez du code ou des classes qui englobent l'accès aux données avec un modèle logique. Comment voyez-vous la conception de l'application à ce niveau est, à mon avis, le meilleur indicateur de la manière dont votre base de données devrait idéalement être structurée.

En général, cela signifie qu'un meilleur mappage des objets de domaine vers les tables de base de données constitue le meilleur point de départ.

Je pense qu'il est préférable d'illustrer ce que je veux dire par exemple:

Cas 1: une classe / une table

Vous pensez en termes de classe Joueur . Player.authenticate, Player.logged_in ?, Player.status, Player.hi_score, Player.gold_credits. Tant que toutes ces actions concernent un seul utilisateur ou représentent des attributs à valeur unique d'un utilisateur, une seule table devrait constituer votre point de départ du point de vue de la conception d'une application logique. Classe unique (joueur), table unique (joueurs).

Cas 2: plusieurs classes / une ou plusieurs tables

Je n'aime peut-être pas la conception de la classe Player , mais je préfère penser en termes de utilisateur , Inventaire . , Tableau de bord et Compte . User.authenticate, User.logged_in ?, User- & account.status, Scoreboard.hi_score (utilisateur), User- > account.gold_credits.

Je le fais probablement parce que je pense que la séparation de ces concepts dans le modèle de domaine rendra mon code plus clair et plus facile à écrire. Dans ce cas, le meilleur point de départ est un mappage direct des classes de domaine sur des tables individuelles: utilisateurs, inventaire, scores, comptes, etc.

Rendre pratique l'idéal

J'ai glissé quelques mots ci-dessus pour indiquer que le mappage un-un des objets de domaine aux tables est la conception idéale, logique .

C'est mon point de départ préféré. Essayer d'être trop intelligent dès le départ - comme de mapper plusieurs classes sur une seule table - a plutôt tendance à créer des problèmes que je préférerais éviter (notamment les blocages et les problèmes de simultanéité).

Mais ce n’est pas toujours la fin de l’histoire. Il existe des considérations pratiques qui peuvent signifier qu'une activité distincte visant à optimiser le modèle de données physique est nécessaire, par exemple. problèmes de simultanéité / verrouillage? la taille de la ligne devient trop grande? indexation des frais généraux? requête de performance, etc.

Cependant, soyez prudent lorsque vous pensez à la performance: ce n’est peut-être pas une ligne dans un tableau, cela ne signifie probablement pas qu’on le demande une seule fois pour obtenir la ligne entière. J'imagine que le scénario de jeu multi-utilisateurs peut impliquer toute une série d'appels pour obtenir des informations sur les joueurs différents à des moments différents, et le joueur veut toujours la "valeur actuelle" qui peut être assez dynamique. Cela peut se traduire par de nombreux appels pour seulement quelques colonnes dans la table (dans ce cas, une conception à plusieurs tables pourrait être plus rapide qu'une conception à une seule table).

Je recommande de les séparer. Pas pour des raisons de base de données, mais pour des raisons de développement.

Lorsque vous codez votre module de connexion de lecteur, vous ne voulez penser à aucune information de jeu. Et réciproquement. Il sera plus facile de séparer les préoccupations si les tableaux sont distincts.

Cela se résume au fait que votre module d’informations de jeu n’a aucune activité professionnelle à régler avec la table de connexion des joueurs.

Probablement aussi bien de le placer dans une table dans cette situation car les performances de votre requête seront meilleures.

Vous ne voulez vraiment utiliser le tableau suivant que lorsqu'il y aura un élément de données en double, grâce auquel vous devriez vous tourner vers la normalisation pour réduire les frais généraux de stockage de vos données.

Je suis d'accord avec la réponse de Thomas Padron-McCarthy :

La solution pour le cas de milliers d’utilisateurs simultanés n’est pas votre problème. Amener les gens à jouer à votre jeu est le problème. Commencez avec la conception la plus simple: faites le jeu correctement, jouez-le et développez-le facilement. Si le jeu décolle, vous dé-normalisez.

Il est également intéressant de noter que les index peuvent être considérés comme des tables séparées contenant une vue plus étroite des données.

Les gens ont pu déduire un design utilisé par Blizzard pour World of Warcraft. Il semble que les quêtes sur lesquelles se trouve un joueur sont stockées avec le personnage. par exemple:

CREATE TABLE Players (
   PlayerID int,
   PlayerName varchar(50),
   ...
   Quest1ID int,
   Quest2ID int,
   Quest3ID int,
   ...
   Quest10ID int,
   ...
)

Initialement, vous pouviez participer à 10 quêtes au maximum, puis à 25, par exemple:

CREATE TABLE Players (
   PlayerID int,
   PlayerName varchar(50),
   ...
   Quest1ID int,
   Quest2ID int,
   Quest3ID int,
   ...
   Quest25ID int,
   ...
)

Cela contraste avec un design en deux parties:

CREATE TABLE Players (
   PlayerID int,
   PlayerName varchar(50),
   ...
)

CREATE TABLE PlayerQuests (
   PlayerID int,
   QuestID int
)

Ce dernier concept est beaucoup plus facile à gérer et permet un nombre arbitraire de quêtes. D'autre part, vous devez rejoindre Players et PlayerQuests afin d'obtenir toutes ces informations.

Je suggérerais de conserver chaque type d'enregistrement dans sa propre table.

Les connexions de lecteur sont un type d’enregistrement. L'inventaire est un autre. Ils ne doivent pas partager une table car la plupart des informations ne sont pas partagées. Gardez vos tables simples en séparant les informations non liées.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top