Денормализация, чтобы уменьшить использование агрегатных функций (вычислений), таких как COUNT и AVG: стоит? - PullRequest
1 голос
/ 30 марта 2020

Я пытаюсь добавить некоторые оптимизации в мои запросы листинга. Некоторые из них имеют 4+ объединения (некоторые OUTER JOIN), требуют агрегатных функций (COUNT, AVG) с GROUP BY, и иногда их необходимо отсортировать по этим вычисленным значениям. Я вижу, что "временный" и "файловая сортировка" оба используются.

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

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

Предположим, что мы в настоящее время нормализовали:

SELECT p.id, p.product_name, p.time_created, COALESCE(AVG(r.rating),0) AS rating, COUNT(r.rating) AS review_count
FROM product AS p LEFT JOIN review AS r ON p.id = r.product_id 
GROUP BY p.id HAVING (rating, time_created) < (?, ?)
ORDER BY rating DESC, time_created DESC
LIMIT ?

Денормализовано, это может быть:

SELECT id, product_name, time_created, rating, review_count
FROM product WHERE (rating, time_created) < (?, ?)
ORDER BY rating DESC, time_created DESC
LIMIT ?

с индексом на два поля.

Денормализованный, безусловно, быстрее читается. Но писать это, конечно, медленнее. У меня такой вопрос: стоит ли это того?

Если я это сделаю, я должен

  1. добавить хранимую процедуру для вычисления избыточных полей при вставке данных,
  2. добавить запланированное событие для их вычисления или
  3. просто позволить программе рассчитать его, потому что скорость не важна (хотя нагрузка на БД может быть)?

РЕДАКТИРОВАТЬ:

Я реализовал это и добавил пару триггеров на источник, чтобы вычислять представление при каждом его изменении. Чтение может быть на 40% быстрее (с 50 строками тестовых данных, надеюсь, будет лучше масштабироваться, так как «временная» таблица не используется). Это может быть самый простой способ реализовать это. Вставка медленнее. Я сохраняю обе версии и буду следить за реальными данными.

Один из добавленных мной триггеров выглядит примерно так:

CREATE TRIGGER review_insert AFTER INSERT ON product_review FOR EACH ROW
UPDATE product AS p
SET p.rating = 
(SELECT COALESCE(AVG(r.rating),0) FROM product_review AS r WHERE r.product_id = p.id)
WHERE p.id = NEW.product_id;

(отредактировано из-за отсутствия в триггере WHERE)

1 Ответ

1 голос
/ 30 марта 2020

Это слишком долго для комментария.

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

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

Я бы рекомендовал хранить данные анализа в другой базе данных, предпочтительно на другом сервере. В общем, я бы рекомендовал использовать периодические периоды c (скажем, ежедневно) для обновления / замены данных. Они могут быть запланированы, когда основная система находится в состоянии покоя. Вы можете использовать триггеры или подобные конструкции для поддержания актуальности данных в реальном времени.

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