Нормализовать или не нормализовать user_ids - PullRequest
1 голос
/ 24 декабря 2009

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

Должен ли я денормализовать user_id и включить его в каждую таблицу для повышения производительности?


Вот один пример:

  • Адрес принадлежит пользователю и имеет идентификатор_пользователя
  • Конверт принадлежит пользователю и имеет идентификатор_пользователя
  • AddressesEnvelopes объединяет Address и Envelope, поэтому у него есть envelope_id и address_id - у него нет user_id, но он может попасть в него либо через конверт, либо по адресу (который должен принадлежать одному и тому же пользователю).

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


Вот другой сценарий:

  • Письмо принадлежит пользователю и имеет идентификатор_пользователя
  • Получатель принадлежит Letter и имеет letter_id
  • RecepientOption принадлежит Recepient и имеет recepient_id

Имеет ли смысл дублировать user_id как в Recepient, так и в RecepientOption, хотя я всегда мог добраться до него, пройдя через ассоциации через Letter?


Некоторые заметки:

  • Нет объектов, которые делится между пользователями. Все иерархия связанных объектов всегда принадлежит тому же пользователю.
  • Пользователь-владелец объектов никогда не меняется.
  • Производительность базы данных важна, потому что это приложение с интенсивным использованием данных. Есть много запросов и много таблиц.

Так должен ли я включать user_id в каждую таблицу, чтобы я мог использовать его при создании индексов? Или это плохой дизайн?

Ответы [ 3 ]

2 голосов
/ 24 декабря 2009

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

user(
    #user_id
)
address(
    #user_id
,   #addres_num
)
envelope(
    #user_id
,   #envelope_num
)
address_envelope(
    #user_id
,   #addres_num
,   #envelope_num
)

(# обозначает столбец первичного ключа)

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

Другая вещь, которая имела бы смысл в этом типе дизайна, - это использование кластеризованных индексов (в MySQL первичный ключ таблиц InnoDB создается из кластеризованного индекса). Если вы убедитесь, что user_id всегда является первым столбцом в вашем индексе, это гарантирует, что для каждой таблицы все данные для одного пользователя хранятся близко друг к другу на диске. Это замечательно, когда вы всегда делаете запрос по user_id, но это может ухудшить производительность, если вы делаете запрос по другому объекту (в этом случае лучшим решением может стать дублирование, такое как у вас sugessted)

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

1 голос
/ 24 декабря 2009

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

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

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

1 голос
/ 24 декабря 2009

Пока вы

а) получить ощутимое улучшение производительности

и

b) знать, какие части вашей базы данных являются реальными нормализованными данными, а какие являются избыточными улучшениями

нет причин не делать этого!

...