Хранение против расчета совокупных значений

96

Существуют ли какие-либо руководящие принципы или практические правила для определения, когда хранить совокупные значения и когда рассчитывать их на лету?

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

Ratings       Widgets
---------     -------
widget_id     widget_id
user_id       name              
rating        avg_rating  <--- The column in question
BENV
источник

Ответы:

58

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

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

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

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

Брайан Баллсун-Стэнтон
источник
Я нашел это обсуждение очень полезным в отношении Материализованных взглядов. Он ориентирован на Oracle, но может быть понят в общем. Для тех, кто, как я, пришел из MySQL, представление MySQL отличается от представления с материализацией, оно является виртуальным и не сохраняется на диске (о чем говорилось в приведенной мной ссылке).
Сиддхартха
upvoted! собирался задать точный вопрос, мне нужно хранить такие индикаторы, как SMA, EMA, WMA, RSI и т. д., и они включают в себя тяжелые вычисления, я составлял таблицу, которую я до сих пор обновлял вручную, эти индикаторы меняются на 100% каждый раз с поступают новые данные, что является хорошей стратегией для их сохранения, я знаю, что представления полностью разорвут базу данных, если все начнут запрашивать представления слева и справа
PirateApp
11

Как часто вам нужно вычислять / отображать значения относительно того, как часто базовые числа меняются / обновляются.

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

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

Джо
источник
каждые 15 минут, 10 показателей, которые меняются на 100% при 1000 строках на показатель
PirateApp
1
@PirateApp и сколько раз он просматривается в среднем за 15 минут? То, что вы также можете сделать, это сгенерировать его по первому запросу в 15-минутном окне, а затем кэшировать его для людей, которые продолжают выполнять перезагрузку снова и снова
Джо
это будет на веб-сайте, поэтому я предполагаю, что по крайней мере 10000 человек будут видеть его для начала, сайт не работает, поэтому у него нет фактических данных о поведении пользователей
PirateApp
1
Вопрос в том, сколько запросов относительно того, как часто они меняются. Так что, если вы предварительно сгенерируете что-то, что будет видно 10000 раз до изменения базовых данных, то да, предварительно сгенерируйте это. Если он просматривается только один раз или менее одного раза (потому что данные изменяются очень быстро или из-за того, что страница просматривается редко), то это не так.
Джо
4

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

  • только что прочитал,
  • в конце месяца,
  • для некоторых пользователей в начале дня
  • ...
Гарик
источник
1
Как они попадают в устаревшую очередь?
Jcolebrand
2
@jcolebrand .. в момент вставки / удаления рейтинга (таблицы рейтингов) для какого-то виджета. В настоящее время среднее значение в таблице виджетов становится недействительным, поэтому мы должны вставить в таблицу запись StaleWidgets, в которой есть только один столбец - widget_id. Используйте триггер или сохраненный процесс, который вставляет запись в таблицу рейтингов или ваш вариант, конечно.
Гарик
2

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

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

techExplorer
источник
2

Для конкретного случая есть другое решение, где вам не нужно добавлять все рейтинги и делить их на общую сумму, чтобы найти среднее значение. Вместо этого у вас может быть другое поле, которое содержит общее количество обзоров, поэтому каждый раз, когда вы добавляете рейтинг, вы вычисляете новое среднее значение, используя (avg_rating × total + new_rating) / total, это намного быстрее, чем агрегат, и уменьшает показания диска, так как вы не должен иметь доступ ко всем значениям рейтинга. Подобные решения могут применяться в других случаях.

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

Адриан Мартинес
источник