Проектирование базы данных - хранить состояние или вычислять состояние каждый раз?

17

Допустим, у меня есть приложение для реляционной базы данных, объект «пользователь» и объект «сообщение». Теперь я хочу показать количество непрочитанных сообщений этому пользователю.

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

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

Как это обычно делается или какой подход лучше?

январь
источник
1
Зависит от ряда факторов: разделена ли ваша БД? Сколько строк / пользователя вы ожидаете? Какой размер общей базы данных вы ожидаете (или сколько всего пользователей)? Сколько запросов в секунду вы ожидаете? Все это не должно быть точным, но некоторые грубые идеи ...
Омер Икбал
10
+1 Это классический вопрос о реляционных базах данных. Нормализовать или не нормализовать? Вот в чем вопрос. Является ли в этой схеме благороднее страдать от строп и стрел возмутительного дублирования, или взяться за триггеры и, применяя, покончить с ними?
Росс Паттерсон,
Я спорю, если это не относится к классическому Rel. дб. вопрос, на сайте уже должен быть ответ, он должен быть закрыт как DUP, или у нас нет ответа, и его следует оставить открытым.
Mattnz

Ответы:

14

Как это обычно делается или какой подход лучше?

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

Док Браун
источник
2
Лучший подход состоит в том, чтобы (сначала используйте более простой метод). Общие правила лучше, чем специфика. (+1 за «тест!», Хотя.)
DougM
9

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

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

Philipp
источник
4
Проблема согласованности легко решается с помощью триггеров, которые настраивают счетчик INSERTили DELETE. (Или UPDATEдля учета смены владельца сообщения.). Хорошая СУБД выполнит операцию и запустит триггеры в одной и той же транзакции, поэтому все или ничего не произойдет.
Blrfl
4

Потенциальная проблема - это производительность, а у вас пока нет проблем с производительностью. Есть много вещей, которые вы можете сделать в зависимости от выбранной базы данных, чтобы справиться с этим в решении № 1: индексирование, аппаратное обеспечение, кэширование и т. Д. Все это зависит от того, как часто пользователь должен получать текущий счетчик непрочитанных сообщений. Многие из этих вариантов не требуют настраиваемого кодирования на стороне приложения, поэтому вы можете реализовать их с изменением кода или совсем без него. Облегчает рост с приложением.

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

Совершаете поездку в БД каждый раз, когда читается сообщение, чтобы пометить IsRead? поля достаточно без пересчета другого поля.

С решением № 2 (ведение счета в поле / на диске), вам понадобится подпрограмма, чтобы периодически перестраивать / пересчитывать это поле при возникновении проблемы? И всегда есть проблемы. Собираетесь ли вы обернуть все это в транзакцию? Каждый раз, когда кто-то отправляет кому-то сообщение, он может потерпеть неудачу, потому что не может обновить UnreadCount принимающего пользователя из-за блокировки таблицы User? Или вы собираетесь создать отдельную таблицу для этого поля?

JeffO
источник
+1 за упоминание проблем с производительностью при обновлении полей
счетчиков
0

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

Затем, как говорит Док, измерьте эффективность этого подхода, и тогда вы сможете определить, нужно ли вам идти другим путем.

Хосе Б
источник