Вопрос

В свободное время я начал писать небольшую многопользовательскую игру с базой данных.Я хотел отделить информацию для входа в систему игрока от другой информации в игре (инвентарь, статистика и статус), и мой друг заметил, что это, возможно, не лучшая идея.

Может быть, лучше свести все в одну таблицу?

Это было полезно?

Решение

Начните с игнорирования производительности и просто создайте наиболее логичный и простой для понимания дизайн базы данных, который вы можете. Если игроки и игровые объекты (мечи? Шахматные фигуры?) Концептуально являются отдельными вещами, то поместите их в отдельные таблицы. Если игрок может нести вещи, вы вставляете внешний ключ в «вещи» таблица, которая ссылается на «игроков» Таблица. И так далее.

Затем, когда у вас есть сотни игроков и тысячи вещей, и игроки бегают по игре и делают вещи, которые требуют поиска в базе данных, ваш дизайн будет все еще достаточно быстрым, если вы просто добавляете соответствующие индексы.

Конечно, если вы планируете тысячи одновременных игроков, каждый из которых инвентаризирует свои вещи каждую секунду, или, возможно, какая-то другая огромная нагрузка поисков в базе данных (поиск каждого кадра, отображаемого графическим движком?), тогда вам понадобится думать о производительности. Но в этом случае обычная дисковая реляционная база данных в любом случае не будет достаточно быстрой, независимо от того, как вы разрабатываете свои таблицы.

Другие советы

Реляционные базы данных работают на наборах, запрос к базе данных не дает ни одного ("не найдено") или большего количества результатов. Реляционная база данных не предназначена для доставки всегда точно одного результата путем выполнения запроса (по отношениям).

Сказал, что хочу отметить, что есть и настоящая жизнь ;-). Отношение «один к одному» может иметь смысл, т. Е. Если число столбцов таблицы достигает критического уровня, разделение этой таблицы может быть быстрее. Но такого рода условия действительно редки.

Если вам не нужно разбивать таблицу, просто не делайте этого. По крайней мере, я предполагаю, что в вашем случае вам придется сделать выбор из двух таблиц, чтобы получить все данные. Это, вероятно, медленнее, чем хранить все в одной таблице. И ваша логика программирования станет, по крайней мере, немного менее читаемой.

Эдит говорит : Комментируя нормализацию: ну, я никогда не видел, чтобы база данных нормализовалась до максимума, и никто не рекомендует это в качестве опции. В любом случае, если вы точно не знаете, будут ли впоследствии нормализованы какие-либо столбцы (т. Е. Помещены в другие таблицы), то это также будет проще сделать с одной таблицей вместо «таблица» -one-to-one- ». другая таблица "

Стоит ли сохранять

Информация о входе в систему игрока [отдельно] от другой в игре (инвентарь, статистика и статус)

часто является вопросом нормализации.Возможно, вы захотите взглянуть на википедию (Третья нормальная форма, Денормализация) определения.Разделите ли вы их на несколько таблиц, зависит от того, какие данные хранятся.Расширение вашего вопроса сделает его конкретным.Данные, которые мы хотим сохранить:

  • имя пользователя:уникальное имя, позволяющее пользователю войти в систему
  • пароль:пароль:связанный с именем пользователя, который гарантирует уникальность пользователя
  • электронная почта:адрес электронной почты пользователя;чтобы игра могла «тыкать» пользователя, когда он в последнее время не играл
  • характер:возможно, отдельное игровое имя для персонажа
  • положение дел:активен ли игрок и/или персонаж в данный момент
  • очки:пример характеристик персонажа

Мы могли бы сохранить это как одну таблицу или как несколько таблиц.

Пример одной таблицы

Если в этом игровом мире у игроков есть один персонаж, то может подойти одна таблица.Например,

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

Это позволит вам найти всю необходимую информацию об игроке с помощью одного запроса в таблице игроков.

Пример нескольких таблиц

Однако, если вы хотите, чтобы у одного игрока было несколько разных персонажей, вам нужно разделить эти данные на две разные таблицы.Например, вы можете сделать следующее:

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

Этот формат требует объединения при просмотре информации как об игроке, так и о персонаже;однако это снижает вероятность возникновения несогласованности при обновлении записей.Последний аргумент обычно используется при защите нескольких таблиц.Например, если у вас есть игрок (LotRFan) с двумя персонажами (голлум, фродо) в формате одной таблицы, у вас будут дублироваться поля имени пользователя, пароля и адреса электронной почты.Если игрок захочет изменить свой адрес электронной почты/пароль, вам потребуется изменить обе записи.Если была изменена только одна запись, таблица станет противоречивой.Однако, если LotRFan захочет изменить свой пароль в формате нескольких таблиц, обновится одна строка в таблице.

Другие соображения

Использовать ли одну таблицу или несколько таблиц зависит от базовых данных.Что здесь не описано, но было отмечено в других ответах, так это то, что при оптимизации часто берутся две таблицы и объединяются их в одну.

Также интересно знать типы изменений, которые будут внесены.Например, если вы решите использовать одну таблицу для игроков, у которых может быть несколько персонажей и которые хотят отслеживать общее количество команд, отдаваемых игроком, путем увеличения поля в таблице игроков;это приведет к обновлению большего количества строк в примере с одной таблицей.

Инвентаризация в любом случае является отношением многие-к-одному, поэтому, вероятно, заслуживает отдельной таблицы (по крайней мере, индексированной по внешнему ключу).

Вы также можете захотеть иметь личные материалы для каждого пользователя отдельно от общедоступных материалов (то есть то, что другие могут видеть о вас). Это может обеспечить лучшие попадания в кеш, поскольку многие люди увидят ваши общедоступные атрибуты, но только вы увидите ваши личные атрибуты.

Отношение 1-1 - это просто вертикальное разделение. Ничего больше. сделайте это, если по какой-то причине вы не будете хранить все данные в одной таблице (например, для разделения на несколько серверов и т. д.)

Как вы можете видеть, общий консенсус по этому вопросу заключается в том, что "это зависит". Оптимизация производительности / запросов легко приходит на ум - как упоминалось @Campbell и другими.

Но я думаю, что для той базы данных, которую вы создаете, лучше всего начать с рассмотрения общего дизайна приложения. Для баз данных для конкретных приложений (в отличие от баз данных, предназначенных для использования со многими приложениями или инструментами отчетности), «идеальная» модель данных, вероятно, лучше всего соответствует модели вашего домена, реализованной в коде вашего приложения.

Вы не можете называть свой дизайн MVC , и я этого не делаю Я знаю, какой язык вы используете, но я ожидаю, что у вас есть некоторый код или классы, которые оборачивают доступ к данным логической моделью. Как вы оцениваете дизайн приложения на этом уровне, я думаю, лучший показатель того, как ваша база данных должна в идеале быть структурирована.

Как правило, это означает, что сопоставление доменных объектов с таблицами базы данных - это одно из лучших мест.

Я думаю, что лучше всего проиллюстрировать, что я имею в виду, на примере:

Случай 1: один класс / одна таблица

Вы думаете с точки зрения класса Player . Player.authenticate, Player.logged_in ?, Player.status, Player.hi_score, Player.gold_credits. Пока все эти действия являются действиями для одного пользователя или представляют собой однозначные атрибуты пользователя, единственная таблица должна быть отправной точкой с точки зрения логического проектирования приложения. Один класс (игрок), один стол (игроки).

Случай 2: несколько классов / одна или несколько таблиц

Возможно, мне не нравится дизайн класса Player , принадлежащий швейцарскому армейскому ножу, но я предпочитаю думать в терминах пользователя , инвентаря , Табло и Аккаунт . User.authenticate, User.logged_in ?, User- > account.status, Scoreboard.hi_score (Пользователь), User- > account.gold_credits.

Я, вероятно, делаю это, потому что думаю, что разделение этих понятий в модели предметной области сделает мой код более понятным и легким для написания. В этом случае наилучшей отправной точкой является прямое сопоставление классов домена с отдельными таблицами: пользователи, инвентарь, результаты, счета и т. Д.

Создание «идеального» на практике

Я добавил несколько слов выше, чтобы указать, что однозначное сопоставление объектов домена с таблицами является идеальным, логическим дизайном.

Это моя предпочтительная отправная точка. Попытка быть слишком умной с самого начала - как сопоставление нескольких классов обратно в одну таблицу - имеет тенденцию просто вызывать проблемы, которых я предпочел бы избежать (особенно взаимоблокировки и проблемы параллелизма).

Но это не всегда конец истории. Существуют практические соображения, которые могут означать, что для оптимизации физической модели данных может потребоваться отдельное действие, например, проблемы параллелизма / блокировки? размер строки становится слишком большим? издержки индексации? производительность запросов и т. д.

Будьте осторожны, думая о производительности: просто потому, что это может быть одна строка в одной таблице, вероятно, это не означает, что она запрашивается только один раз, чтобы получить всю строку. Я полагаю, что сценарий многопользовательской игры может включать в себя целый ряд вызовов для получения различной информации об игроке в разное время, и игрок всегда хочет получить «текущую стоимость», которая может быть довольно динамичной. Это может привести ко многим вызовам только для нескольких столбцов в таблице каждый раз (в этом случае дизайн с несколькими таблицами может быть быстрее, чем дизайн с одной таблицей).

Я рекомендую разделить их. Не по каким-либо причинам, связанным с базой данных, а по причинам развития.

Когда вы кодируете свой модуль входа в систему игрока, вам не нужно думать о какой-либо информации об игре. И наоборот. Будет легче поддерживать разделение интересов, если таблицы различны.

Это сводится к тому, что в вашем информационном модуле по игре нет проблем с таблицей входа игрока.

Возможно, в этой ситуации также можно поместить его в одну таблицу, поскольку производительность вашего запроса будет выше.

Вы действительно хотите использовать следующую таблицу только в том случае, если будет элемент дубликатов данных, с помощью которого вы должны обратиться к нормализации, чтобы уменьшить накладные расходы на хранение данных.

Я согласен с ответом Томаса Падрона-Маккарти :

Решение проблемы тысяч одновременных пользователей - не ваша проблема. Заставить людей играть в твою игру - это проблема. Начните с самого простого дизайна - сделайте игру, работающей, играбельной и легко расширяемой. Если игра вылетает, значит, вы нормализуетесь.

Стоит также упомянуть, что индексы можно рассматривать как отдельные таблицы, которые содержат более узкое представление данных.

Люди смогли сделать вывод о дизайне, используемом Blizzard для World of Warcraft. Кажется, что квесты, на которых игрок находится, сохраняются вместе с персонажем. например:.

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

Изначально вы могли принять участие не более чем в 10 квестах, позже он был расширен до 25, например:

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

Это в отличие от раздельного дизайна:

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

CREATE TABLE PlayerQuests (
   PlayerID int,
   QuestID int
)

С последним концептуально намного легче иметь дело, и он допускает произвольное количество квестов. С другой стороны, вы должны присоединиться к игрокам и квестам игроков, чтобы получить всю эту информацию.

Я бы посоветовал вам хранить каждый тип записи в отдельной таблице.

Логины игроков - это один из типов записей. Инвентарь это другое. Они не должны делиться таблицей, так как большая часть информации не передается. Сохраняйте свои таблицы простыми, выделяя несвязанную информацию.

Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top