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


Возможно, в этой ситуации лучше поместить его в одну таблицу, так как производительность вашего запроса будет лучше.
Вы действительно хотите использовать следующую таблицу только тогда, когда будет элемент повторяющихся данных, с помощью которого вам следует обратить внимание на нормализацию, чтобы уменьшить накладные расходы на хранилище данных.
В любом случае инвентаризация - это отношение «многие-к-одному», поэтому, вероятно, заслуживает отдельной таблицы (по крайней мере, проиндексированной по внешнему ключу).
Вы также можете захотеть иметь личные данные для каждого пользователя отдельно от общедоступных (т. Е. Того, что о вас могут видеть другие). Это может обеспечить лучшее обращение к кешу, поскольку многие люди будут видеть ваши общедоступные атрибуты, но только вы видите свои личные атрибуты.
Реляционные базы данных работают с наборами, запрос к базе данных не дает результатов («не найдено») или больше. Реляционная база данных не предназначена для предоставления всегда только одного результата путем выполнения запроса (по отношениям).
Сказал, что хочу упомянуть, что есть и настоящая жизнь ;-). Связь «один-к-одному» мощь имеет смысл, т.е. если количество столбцов в таблице достигает критического уровня, возможно, будет быстрее разделить эту таблицу. Но такие условия действительно редки.
Если вам действительно не нужно разбивать таблицу, просто не делайте этого. По крайней мере, я предполагаю, что в вашем случае вам придется выбрать более двух таблиц, чтобы получить все данные. Вероятно, это медленнее, чем хранить все в одной таблице. И от этого ваша логика программирования станет, по крайней мере, немного менее читаемой.
Эдит говорит: Комментируя нормализацию: Ну, я никогда не видел, чтобы база данных была нормализована до максимального значения, и никто на самом деле не рекомендует это как вариант. В любом случае, если вы точно не знаете, будут ли какие-либо столбцы нормализованы (т.е. помещены в другие таблицы) позже, то это тоже проще сделать с одной таблицей, а не с другой таблицей. "
Я в этом сомневаюсь. Почему должно быть проще? «ALTER TABLE x DROP COLUMN y» одинаково для обеих таблиц. То же самое и с обновлением вашей программы. Где барышка?
«Реляционная база данных не предназначена для выдачи всегда только одного результата при выполнении запроса». <- это не имеет смысла? «SELECT * FROM table WHERE primary_key = something» всегда будет возвращать одну строку (если она существует). Теория множеств не заботится о том, содержит ли набор 0, 1 или m кортежей.
Позвольте мне пояснить: реляционная база данных не предназначена для предоставления всегда только одного результата, выполняя запрос «по отношениям».
Отношения 1-1 - это просто вертикальный раскол. Ничего больше. сделайте это, если по какой-то причине вы не будете хранить все данные в одной таблице (например, для разделения на несколько серверов и т. д.)
В настоящее время он будет работать только на одном сервере. Необходимость масштабирования - это довольно далекое будущее.
Я бы посоветовал вам хранить каждый тип записи в отдельной таблице.
Логины игроков - это один из типов записей. Инвентарь - другое. Они не должны делиться таблицей, так как большая часть информации не передается. Сделайте свои таблицы простыми, отделяя несвязанную информацию.
Как видите, общее мнение по этому поводу сводится к тому, что «это зависит от обстоятельств». На ум легко приходит оптимизация производительности / запросов - как упоминалось @Campbell и другими.
Но я думаю, что для создаваемой вами базы данных для конкретного приложения лучше всего начать с рассмотрения общего дизайна приложения. Для баз данных для конкретных приложений (в отличие от баз данных, предназначенных для использования со многими приложениями или инструментами отчетности) «идеальная» модель данных, вероятно, является той, которая лучше всего соответствует модели вашей предметной области, реализованной в коде вашего приложения.
Вы можете не называть свой дизайн MVC, и я не знаю, какой язык вы используете, но я ожидаю, что у вас есть некоторый код или классы, которые обертывают доступ к данным с помощью логической модели. То, как вы рассматриваете дизайн приложения на этом уровне, я считаю лучшим индикатором того, как ваша база данных должна быть структурирована.
В общем, это означает, что лучше всего начать с однозначного сопоставления объектов домена с таблицами базы данных.
Думаю, лучше всего проиллюстрировать то, что я имею в виду, на примере:
Вы думаете в терминах класса Игрок. Player.authenticate, Player.logged_in ?, Player.status, Player.hi_score, Player.gold_credits. Если все это действия для одного пользователя или представляют собой однозначные атрибуты пользователя, то единая таблица должна быть вашей отправной точкой с точки зрения логического дизайна приложения. Одиночный класс (игрок), одиночный стол (игроки).
Возможно, мне не нравится дизайн класса швейцарского армейского ножа Игрок, но я предпочитаю думать в терминах Пользователь, Инвентарь, Табло и Счет. User.authenticate, User.logged_in ?, User-> account.status, Scoreboard.hi_score (Пользователь), User-> account.gold_credits.
Я, вероятно, делаю это, потому что думаю, что разделение этих концепций в модели предметной области сделает мой код более понятным и легким для написания. В этом случае лучшей отправной точкой является прямое сопоставление классов предметной области с отдельными таблицами: Пользователи, Инвентарь, Оценки, Учетные записи и т. д.
Я добавил несколько слов выше, чтобы указать, что однозначное отображение объектов домена в таблицы - это дизайн идеальный, логичный.
Это моя предпочтительная отправная точка. Попытка быть слишком умной - например, сопоставление нескольких классов с одной таблицей - имеет тенденцию просто вызывать проблемы, которых я бы предпочел избежать (особенно проблемы с взаимоблокировками и параллелизмом).
Но это не всегда конец истории. Существуют практические соображения, которые могут означать необходимость отдельной деятельности по оптимизации физической модели данных, например: Проблемы с параллелизмом / блокировкой? размер строки становится слишком большим? накладные расходы на индексацию? производительность запросов и т. д.
Однако будьте осторожны, думая о производительности: то, что это может быть одна строка в одной таблице, вероятно, не означает, что она запрашивается только один раз, чтобы получить всю строку. Я предполагаю, что сценарий многопользовательской игры может включать в себя целую серию вызовов для получения различной информации об игроке в разное время, и игрок всегда хочет «текущее значение», которое может быть довольно динамичным. Это может привести к множеству вызовов для нескольких столбцов в таблице каждый раз (в этом случае дизайн с несколькими таблицами может быть быстрее, чем дизайн с одной таблицей).
Начните с игнорирования производительности и просто создайте максимально логичный и простой для понимания дизайн базы данных. Если игроки и игровые объекты (мечи? Шахматные фигуры?) Концептуально представляют собой разные вещи, то поместите их в отдельные таблицы. Если игрок может носить вещи, вы помещаете внешний ключ в таблицу «вещи», которая ссылается на таблицу «игроков». И так далее.
Затем, когда у вас есть сотни игроков и тысячи вещей, и игроки бегают по игре и делают вещи, требующие поиска в базе данных, что ж, ваш дизайн по-прежнему будет достаточно быстрым, если вы просто добавите соответствующие индексы.
Конечно, если вы планируете одновременное присутствие тысяч игроков, каждый из которых будет проводить инвентаризацию своих вещей каждую секунду, или, возможно, какую-то другую огромную нагрузку на поиск в базе данных (поиск каждого кадра, отображаемого графическим движком?), Тогда вам нужно будет подумать о представление. Но в этом случае типичная дисковая реляционная база данных все равно будет недостаточно быстрой, как бы вы ни проектировали свои таблицы.
Сохранять ли
player login information [separate] from other in game information (inventory, stats, and status)
часто вопрос нормализации. Вы можете быстро взглянуть на определения в Википедии (Третья нормальная форма, Денормализация). Разделите ли вы их на несколько таблиц, зависит от того, какие данные хранятся. Расширение вашего вопроса сделает это конкретным. Данные, которые мы хотим сохранить:
Мы могли бы хранить это как одну таблицу или как несколько таблиц.
Пример одной таблицы
Если в этом игровом мире у игроков есть только один персонаж, то один стол может быть подходящим. Например,
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) с двумя персонажами (gollum, frodo) в формате одной таблицы, у вас будут дублироваться поля имени пользователя, пароля и адреса электронной почты. Если игрок хотел изменить свой адрес электронной почты / пароль, вам нужно будет изменить обе записи. Если была изменена только одна запись, таблица станет несогласованной. Однако, если LotRFan хотел изменить свой пароль в формате нескольких таблиц, обновляется одна строка в таблице.
Прочие соображения
Использование одной или нескольких таблиц зависит от базовых данных. Что не было описано здесь, но было отмечено в других ответах, так это то, что при оптимизации часто используются две таблицы и объединяются их в одну таблицу.
Также интересно знать типы изменений, которые будут внесены. Например, если вы выбрали использование одной таблицы для игроков, у которых может быть несколько персонажей, и хотели бы отслеживать общее количество команд, отданных игроком, увеличивая поле в таблице игроков; это приведет к обновлению большего количества строк в примере с одной таблицей.
Рекомендую их разделить. Не по причинам базы данных, а по причинам развития.
Когда вы кодируете свой модуль входа игрока, вы не хотите думать ни о какой информации об игре. И наоборот. Будет легче поддерживать разделение проблем, если таблицы будут разными.
Это сводится к тому, что у вашего модуля игровой информации нет никакого дела, чтобы возиться с таблицей входа игрока.
я согласен с Ответ Томаса Падрона-Маккарти:
Решение для тысяч одновременных пользователей - не ваша проблема. Проблема заключается в том, чтобы заставить людей играть в вашу игру. Начните с простейшего дизайна - сделайте игру, работающую, играбельную и легко расширяемую. Если игра взлетит, значит вы денормализуетесь.
Также стоит упомянуть, что индексы можно рассматривать как отдельные таблицы, содержащие более узкое представление данных.
Люди смогли сделать вывод, что 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
)
С последним гораздо проще иметь дело с концептуальной точки зрения, и он позволяет выполнять произвольное количество квестов. С другой стороны, вы должны присоединиться к Players и PlayerQests, чтобы получить всю эту информацию.
Одна хорошая вещь в том, чтобы держать его отдельно, заключается в том, что вы можете добавлять / удалять поля, если вы когда-либо захотите изменить настройку вашей базы данных. Это сложнее сделать, если все поля встроены в одну таблицу.