Pergunta

No meu tempo livre eu comecei a escrever um pequeno jogo multiplayer com um banco de dados backend. Eu estava olhando para informações separadas jogador de login de outros em informações do jogo (inventário, estatísticas e status) e um amigo trouxe isso pode não ser a melhor idéia.

Seria melhor para fixo tudo junto em uma tabela?

Foi útil?

Solução

Comece por ignorar o desempenho, e apenas criar o design de banco de dados mais lógica e fácil de entender que você pode. Se os jogadores e objetos do jogo (espadas? Partes de xadrez?) São coisas separadas conceitualmente, em seguida, colocá-los em mesas separadas. Se um jogador pode realizar coisas, você coloca uma chave estrangeira em "coisas" a mesa de que as referências a "players" de mesa. E assim por diante.

Então, quando você tem centenas de jogadores e milhares de coisas, e os jogadores correr no jogo e fazer coisas que requerem pesquisas de banco de dados, bem, seu projeto irá ainda ser rápido o suficiente, se basta adicionar os índices apropriados.

Claro que, se você planeja para milhares de jogadores simultâneos, cada um deles inventariar suas coisas a cada segundo, ou talvez algum outro enorme carga de pesquisas de banco de dados (a busca para cada quadro processado pelo motor gráfico?) Então você vai precisar pensar sobre o desempenho. Mas, nesse caso, um banco de dados relacional baseado em disco típico não vai ser rápido o suficiente de qualquer maneira, não importa como você projetar suas tabelas.

Outras dicas

Bancos de dados relacionais trabalhar em conjuntos, uma consulta de banco de dados está lá para entregar nenhum ( "não encontrado") ou mais resultados. Um banco de dados relacional não se destina a fornecer sempre exatamente um resultado por executar uma consulta (sobre as relações).

disse que eu quero mencionar que há vida real, também ;-). A relação de um-para-um força faz sentido, ou seja, se a contagem de coluna da tabela atinge um nível crítico que poderia ser mais rápido para dividir esta tabela. Mas este tipo de condições realmente são raros.

Se você realmente não precisa dividir a tabela simplesmente não fazê-lo. Pelo menos eu assumir que no seu caso, você teria que fazer uma escolha mais de duas tabelas para obter todos os dados. Isso provavelmente é mais lento do que armazenar tudo em uma mesa. E sua lógica de programação, pelo menos, ficar um pouco menos legível ao fazê-lo.

diz Edith : Comentando sobre a normalização: Bem, eu nunca vi um banco de dados normalizado ao seu máximo e ninguém está realmente recomendando que como uma opção. Enfim, se você não sei exatamente se nenhuma coluna será normalizado (ou seja, colocar em outras tabelas), mais tarde, em seguida, ele também é mais fácil de fazer isso com uma tabela em vez de "mesa" -um-to-one "outra tabela "

Se manter

informação do jogador de login [separar] de outros em informações do jogo (inventário, estatísticas, e status)

é muitas vezes uma questão de normalização. Você pode querer dar uma olhada rápida na wikipedia ( Terceira Forma Normal , Desnormalização ) definições. Se você separar estes em várias tabelas depende do que os dados são armazenados. Expandindo a sua pergunta vai fazer este concreto. Os dados que nós queremos loja é:

  • usuário: nome exclusivo que permite que um usuário para acessar o sistema
  • passwd: password associada com o nome de usuário que garante o usuário é único
  • e-mail: O endereço de e-mail para o usuário; assim o jogo pode "picar" o usuário quando eles não jogaram recentemente
  • caracteres: nome ingame possivelmente separado para o personagem
  • status: é o jogador e / ou o caráter ativo
  • pontos de vida: um exemplo de estatísticas para o personagem

Podemos armazenar isso como uma tabela única ou como várias tabelas.

Exemplo Tabela Individual

Se os jogadores têm um único personagem neste mundo de jogo, em seguida, uma única tabela pode ser apropriado. Por exemplo,

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)
);

Isso permitirá que você para encontrar todas as informações pertinentes sobre um jogador com uma única consulta na tabela de jogadores.

múltipla Exemplo Tabela

No entanto, se você quiser permitir que um único jogador a ter vários personagens diferentes que você gostaria de dividir este dados em duas tabelas diferentes. Por exemplo, você pode fazer o seguinte:

-- 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);

Este formato exige que se junta ao olhar para a informação tanto para o jogador e caráter; no entanto, faz registros de atualização menos propensos a resultar em inconsistência. Este último argumento é normalmente utilizado quando advogando para várias tabelas. Por exemplo, se você tivesse um jogador (LOTRfan) com dois caracteres (Gollum, Frodo) no formato de tabela única, você teria os campos nome de usuário, senha, e-mail duplicados. Se o jogador queria mudar o seu e-mail / senha, você precisará modificar os registros. Se apenas um registro foi modificado a mesa vai se tornar inconsistente. No entanto, se LOTRfan queria mudar sua senha no formato de tabela múltipla uma única linha na tabela é atualizada.

Outras considerações

Se usar uma única tabela ou várias tabelas depende dos dados subjacentes. O que não foi descrito aqui, mas foi observado em outras respostas é que as otimizações muitas vezes terá duas mesas e combiná-los em uma única tabela.

Também de interesse é saber os tipos de alterações que serão feitas. Por exemplo, se você tivesse que optar por usar uma única tabela para os jogadores que podem ter vários personagens e queria manter o controle do número total de comandos emitidos pelo jogador incrementando um campo na tabela de jogadores; isso resultaria em mais linhas a ser atualizado na tabela de exemplo único.

Inventory é um relacionamento muitos-para-um de qualquer maneira, então provavelmente merece sua própria tabela (indexado em sua chave estrangeira, pelo menos).

Você também pode querer ter coisas pessoais para cada usuário separado do material público (ou seja, o que os outros podem ver de você). Que podem proporcionar melhores cache-hits, já que muitas pessoas vão ver os seus atributos públicos, mas só você ver os seus atributos pessoais.

A relação de 1-1 é apenas uma divisão vertical. Nada mais. fazê-lo se você por algum motivo você não vai armazenar todos os dados na mesma tabela (digamos, para particionar em vários servidores, etc)

Como você pode ver, o consenso geral sobre isto é que "depende". Performance / consulta otimização facilmente vem à mente -. Como mencionado por @Campbell e outros

Mas eu acho que para o tipo de banco de dados específico do aplicativo que você está construindo, o melhor é começar por considerar o desenho geral do aplicativo. Para bancos de dados específicos do aplicativo (em oposição às bases de dados projetado para ser usado com muitas aplicações ou ferramentas de relatórios), o modelo de dados 'ideal' é provavelmente a que melhores mapas para o seu modelo de domínio, como implementado no código do aplicativo.

Você não pode chamar seu projeto MVC , e eu não sabe o idioma que você está usando, mas eu espero que você tem algum código ou classes que envolve o acesso a dados com um modelo lógico. Como você vê o design do aplicativo, a este nível é que eu acho que o melhor indicador de como seu banco de dados deve idealmente ser estruturado.

Em geral, isso significa um mapeamento um-para-um dos objetos de domínio para tabelas de banco de dados é o melhor lugar para começar.

Eu acho melhor para ilustrar o que quero dizer com exemplo:

Caso 1: Uma Classe / Uma Tabela

Você pensa em termos de uma Jogador classe. Player.authenticate, Player.logged_in ?, Player.status, Player.hi_score, Player.gold_credits. Enquanto tudo isso são ações em um único usuário, ou representam atributos de valor único de um usuário, em seguida, uma única tabela deve ser seu ponto de partida de uma perspectiva de design do aplicativo lógico. classe única (Player), mesa única (jogadores).

Caso 2: Multiple Classe / uma ou mais tabelas

Talvez eu não como o swiss-army-knife Jogador design de classe, mas preferem pensar em termos de usuário , Inventário , Painel e Conta . User.authenticate, User.logged_in ?, Usuário-> account.status, Scoreboard.hi_score (User), Usuário-> account.gold_credits.

Provavelmente estou fazendo isso porque eu acho que separa estes conceitos no modelo de domínio está indo fazer o meu código mais claro e mais fácil de escrever. Neste caso, o ponto de melhor começando um mapeamento direto das classes de domínio para tabelas individuais: Usuários, Inventário, Scores, contas etc

.

Fazendo o 'Ideal' Prático

Eu escorreguei algumas palavras acima para indicar que o um-um mapeamento de objetos de domínio para tabelas é a ideal, lógica design.

Esse é o meu ponto de partida preferido. Tentando ser muito inteligente fora do bastão - como mapear múltiplas classes de volta para uma única tabela -. Tende a apenas convidar problemas eu prefiro evitar (impasses e problemas de concorrência especialmente)

Mas nem sempre é o fim da história. Existem considerações práticas que podem significar um estudo separado para optimizar o modelo de dados físicos são necessários, por exemplo, simultaneidade / questões de bloqueio? tamanho da linha ficando muito grande? indexando sobrecarga? o desempenho da consulta etc.

Tenha cuidado quando se pensa em desempenho, porém: apenas porque pode ser uma linha em uma tabela, provavelmente, não significa que ele é consultado apenas uma vez para obter toda a linha. Imagino que o cenário de jogo multiusuário pode envolver toda uma série de chamadas para obter informações diferentes jogador em momentos diferentes, eo jogador sempre quer o 'valor atual', que pode ser bastante dinâmico. Isso pode se traduzir em muitas chamadas para apenas algumas colunas da tabela de cada vez (nesse caso, um projeto multi-tabela poderia ser mais rápido do que um projeto de mesa única).

Eu recomendo que os separa. Não por quaisquer razões de banco de dados, mas por razões de desenvolvimento.

Quando você está programando o seu módulo de login jogador, você não quer pensar sobre qualquer uma das informações jogo. E vice-versa. Será mais fácil para manter uma separação de interesses se as tabelas são distintos.

Ela resume-se ao fato de que o módulo de informações do jogo não obteve nenhum Messin negócio em torno do quadro de jogador login.

Provavelmente, assim como para colocá-lo em uma tabela nesta situação como seu desempenho de consulta será melhor.

Você realmente só quer usar a tabela seguinte, quando haverá um elemento de dados duplicados pelo qual você deve olhar para a normalização para reduzir suas despesas gerais de armazenamento de dados.

eu concordo com o Thomas Padron-McCarthy resposta :

Resolvendo para o caso de milhares de usuários simultâneos não é o seu problema. Levar as pessoas a jogar o seu jogo é o problema. Comece com o design mais simples - se o jogo terminar, trabalhando, jogável, e facilmente expansível. Se o jogo tira, então você de-normalize.

Também vale a pena mencionar que os índices podem ser encarado como mesas separadas que contêm uma visão mais estreita dos dados.

As pessoas têm sido capazes de inferir um design utilizado pela Blizzard para World of Warcraft. Parece que as missões um jogador fica é armazenado com o personagem. por exemplo:.

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

Inicialmente você poderia estar em, no máximo, 10 missões, mais tarde, foi ampliado para 25, por exemplo:.

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

Este é em contraste com um projeto da separação:

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

CREATE TABLE PlayerQuests (
   PlayerID int,
   QuestID int
)

O último é muito mais fácil conceitualmente para lidar com, e permite que o número arbitrário de missões. Por outro lado você tem que juntar Jogadores e PlayerQuests a fim de obter todas as informações.

Gostaria de sugerir que você mantenha cada tipo de registro em sua própria tabela.

Logins do jogador são um tipo de registro. Inventory é outra. Eles não devem compartilhar uma mesa como a maioria das informações não são compartilhadas. Mantenha o seu tabelas simples, separando as informações independentes.

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top