Что более эффективно: несколько таблиц MySQL или одна большая таблица?

104

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

  • Будет ли это помощью или помехой?
  • Соображения скорости при вызове, обновлении или поиске / манипулировании?

Вот пример некоторых из моих структур таблиц:

  • пользователи - UserId, имя пользователя, адрес электронной почты, зашифрованный пароль, дата регистрации, ip
  • user_details - данные cookie, имя, адрес, контактные данные, принадлежность, демографические данные
  • user_activity - публикации, последний онлайн, последний просмотр
  • user_settings - настройки отображения профиля
  • user_interests - переменные рекламного таргетинга
  • user_levels - права доступа
  • user_stats - хиты, счетчики

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

Большинство таблиц имеют соотношение 1: 1, что и было основной причиной их денормализации.

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

Питер Крейг
источник
Этот другой вопрос тоже может быть полезным
Мости Мостачо

Ответы:

66

Несколько таблиц помогают в следующих случаях / случаях:

(а) если разные люди собираются разрабатывать приложения с разными таблицами, имеет смысл разделить их.

(b) Если вы хотите предоставить разным людям разные полномочия для разных частей сбора данных, может быть удобнее разделить их. (Конечно, вы можете определить представления и соответствующим образом авторизовать их).

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

(d) Меньший размер следа может быть удобен при разработке приложений для сбора конкретных данных одной организации.

(e) Это возможность: то, что вы считали данными с одним значением, в будущем может оказаться действительно несколькими значениями. например, кредитный лимит на данный момент представляет собой поле с одним значением. Но завтра вы можете решить изменить значения как (дата от, дата до, значение кредита). Разделенные столы теперь могут пригодиться.

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

Удачи.

user115905
источник
3
@RohitKhatri: Насколько мне известно, наличие нескольких таблиц в большинстве случаев увеличивает производительность.
Hari Harker
1
@HariHarker Спасибо за ваш ответ, но я понял, что это зависит от вашего шаблона доступа.
Рохит Хатри
До недавнего времени я всегда хранил все данные в одной таблице, но, если подумать, разделение данных имеет много преимуществ с точки зрения производительности (в зависимости от варианта использования, конечно), семантики (некоторые данные лучше сгруппировать в другой стол) и развитие. Например, я прямо сейчас разрабатываю собственную ERP-систему поверх устаревшей системы. Мне пришлось расширить старые таблицы базы данных дополнительными столбцами. Я решил сделать новые таблицы для новых данных. Некоторые новые функции пригодятся для устаревшей системы, и теперь я могу легко интегрировать их, не переписывая слишком много старых запросов,
Ожье Шелвис
35

Объединение таблиц называется денормализацией.

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

MySQLспособен использовать только JOINметод, а именно NESTED LOOPS.

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

Поиск записи - довольно дорогостоящая операция, которая может занять в десятки раз больше времени, чем сканирование чистой записи.

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

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

С другой стороны, адская эксплуатация гарантирована.

Quassnoi
источник
1
Если у вас 10000 пользователей и вы правильно выполняете соединение с базой данных, настроенной с использованием внешних ключей, вам потребуется только интенсивный поиск, выполнив что-то вроде select * from users where name = "bob". Когда у вас есть bob, вы используете индекс для поиска таблиц, соединенных с bob, что значительно быстрее, потому что вы используете идентификатор bob. Это происходит независимо от того, выполняете ли вы соединение в своем запросе или запрашиваете bob, а затем отдельно запрашиваете таблицу. Конечно, надеюсь, ваш второй запрос основан на идентификаторе Боба, а не на чем-то другом.
Руди Гарсия
17

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

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

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

Дэвид Хедлунд
источник
Я должен полностью не согласиться с вашим комментарием о том, что нормализация фокусируется только на аккуратности и полностью игнорирует производительность. В обоих сценариях есть компромисс, и денормализация фактически ставит под угрозу целостность данных. Я бы сказал, что нормализация вашей базы данных на самом деле улучшает общую производительность базы данных, а не дает быстрое незначительное увеличение производительности от денормализованной таблицы.
Руди Гарсия
Учитывая, что речь идет именно об отношениях 1: 1, разделение таблиц не является задачей нормализации , верно? Если нет повторяющейся информации, это нормально, даже если это одна таблица. (Ну, это не может удовлетворить 3NFнормализации, поэтому польза от второй таблицы , чтобы решить , что, но это , кажется, не то , что OP имеет в виду повторно другие таблицы.)
ToolmakerSteve
14

Связаны ли все эти таблицы 1-to-1? Например, будет ли каждая пользовательская строка иметь только одну соответствующую строку в user_statsили user_levels? Если так, возможно, имеет смысл объединить их в одну таблицу. Если же отношений нет 1 to 1 , вероятно, не имеет смысла объединять (денормализовать) их.

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

ETA:

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

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

Эрик Петрелье
источник
Да, каждая таблица имеет только одну строку для каждого пользователя, просто чтобы избавить от головной боли при управлении большим количеством дублированных данных. Вот почему я думаю, что один стол подходит. Если бы пользовательские данные занимали несколько строк, я бы ожидал, что эти таблицы будут отделены от основной пользовательской таблицы.
Питер Крейг,
1
Если каждая таблица имеет отношение 1 к 1, тогда будет проще использовать одну таблицу. В этом случае нет необходимости разделять таблицу. Разделение таблицы предполагает, что имеется более одной строки, что может привести к тому, что другой разработчик будет относиться к ним таким же образом.
Ричард Л.
Очень интересная мысль о применении 80/20 к дизайну таблиц базы данных. Заставил меня задуматься о дизайне классов ООП (я в первую очередь Java-разработчик) и подумал, может ли это быть эффективным там (поместите основные 80% функций приложения в один класс, а остальные в другие классы).
Zack Macomber
1
@ZackMacomber - Нет, разделение классов должно быть основано на локальности ссылки . Преимущество разделения на несколько классов состоит в том, чтобы нарисовать границу вокруг меньшей единицы функциональности, чтобы ее было легче понять / протестировать / изменить, и ясно, где эта единица взаимодействует с другими единицами функциональности. Цель состоит в том, чтобы сохранить большинство соединений (ссылок, вызовов) внутри одного модуля, с небольшим количеством соединений между модулями . Определение нескольких интерфейсов, которые реализует класс, с разными интерфейсами для каждого варианта использования, может быть полезным первым шагом к такому разделению.
ToolmakerSteve
@ToolmakerSteve Хорошие мысли +1
Зак Макомбер
9

Создание одной массивной таблицы противоречит принципам реляционной базы данных. Я бы не стал объединять их все в одну таблицу. Вы собираетесь получить несколько экземпляров повторяющихся данных. Например, если у вашего пользователя три интереса, у вас будет 3 строки с одними и теми же данными пользователя, чтобы сохранить три разных интереса. Определенно используйте подход с несколькими «нормализованными» таблицами. См. Эту страницу Wiki для нормализации базы данных.

Изменить: я обновил свой ответ, так как вы обновили свой вопрос ... Я согласен с моим первоначальным ответом еще больше, поскольку ...

большая часть этих ячеек, вероятно, останется пустой

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

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


источник
Мне нравится этот подход, потому что денормализованные данные существуют только временно, как снимок момента времени. Никаких проблем с вставкой / изменением / удалением - просто выбросьте, когда закончите.
ToolmakerSteve
7

Почему бы не использовать тот же подход, который использует Wordpress, имея таблицу пользователей с базовой информацией о пользователе, которая есть у каждого, а затем добавляя таблицу «user_meta», которая может быть любой парой ключ-значение, связанной с идентификатором пользователя. Поэтому, если вам нужно найти всю метаинформацию для пользователя, вы можете просто добавить ее в свой запрос. Вам также не всегда нужно добавлять дополнительный запрос, если он не нужен для таких вещей, как вход в систему. Преимущество этого подхода также оставляет вашу таблицу открытой для добавления новых функций для ваших пользователей, таких как сохранение их дескрипторов Twitter или каждого отдельного интереса. Вам также не придется иметь дело с лабиринтом связанных идентификаторов, потому что у вас есть одна таблица, которая управляет всеми метаданными, и вы ограничите ее только одной связью вместо 50.

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

Руди Гарсия
источник
Таблица Wordpress wp_usermetaрастет геометрически. Каждый пользователь добавляет в wp_usermetaтаблицу X строк , по одной строке для каждой части метаинформации, которую мы хотим сохранить для этого пользователя. Если вы сохраните 8 настраиваемых полей для каждого пользователя, это означает, что wp_usermeta будет содержать users * 8строки. Похоже, это вызывает проблемы с производительностью, но я не уверен, проблема в этом или нет…
thirdender
1
Я мог понять, как это может вызвать проблемы с производительностью, если у вас десятки тысяч пользователей. По сути, база данных должна будет искать по 10000 * 8 записей в мета-таблице пользователей, чтобы найти те, которые вы ищете. Однако, если вы запрашиваете метаданные только тогда, когда это необходимо, я думаю, ваша производительность будет лучше. Если вы всегда запрашиваете метаданные, даже если они вам не нужны, у вас могут быть проблемы. Если вам всегда нужны метаданные, возможно, разделение таблиц - не лучший подход.
Руди Гарсия
1
Буквально вчера мы имели дело с темой WP, которая загружала всех пользователей (использующих get_users()) только для расчета разбивки на страницы. После того как мы исправили код, чтобы SELECT COUNT(…)вместо этого использовать запрос для разбивки на страницы, время загрузки страницы увеличилось с 28 секунд до примерно 400 мс. Я до сих пор задаюсь вопросом, как сравнивается производительность с объединенными таблицами или одной плоской таблицей… У меня возникли проблемы с поиском каких-либо показателей производительности в Интернете.
thirdender 05
Если подумать о моем предыдущем комментарии, может показаться, что разделение таблицы по-прежнему эффективно, если только по какой-то причине, например, как в приведенном выше примере разбивки на страницы, вам не нужно будет выбрать всех пользователей. Хотя, если вы получаете всю метаинформацию, у вас все равно будет 80 тыс. Записей в таблице usermeta. Это очень много для поиска. Возможно, кто-то сможет проверить, какой подход лучше, запустив сценарий в обеих реализациях и запустив его 100 раз, чтобы получить среднее значение, я мог бы просто сделать это.
Руди Гарсия
1
Я прочитал это снова только сегодня и понял, что мой комментарий о 10000 * 8 записей верен, однако способ работы базы данных должен делать это в основном без проблем. Если бы по какой-то причине вы захватили всех 10000 пользователей, а также их метаинформацию, это было бы смешно. Я не могу придумать ни одного сценария, где бы вы этого захотели База данных легко извлечет мета для одного пользователя с молниеносной скоростью, хотя из-за внешних ключей и индексации. Предполагая, что ваша модель БД настроена правильно.
Руди Гарсия,
5

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

Тундей
источник
1

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

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

Мне кажется, что вы можете пропустить User_Details, поскольку это, вероятно, отношение 1 к 1 с пользователями. Но в остальном, вероятно, много строк на пользователя?

Ричард Л
источник