Я пытаюсь добавить некоторые оптимизации в мои запросы листинга. Некоторые из них имеют 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 ?
с индексом на два поля.
Денормализованный, безусловно, быстрее читается. Но писать это, конечно, медленнее. У меня такой вопрос: стоит ли это того?
Если я это сделаю, я должен
- добавить хранимую процедуру для вычисления избыточных полей при вставке данных,
- добавить запланированное событие для их вычисления или
- просто позволить программе рассчитать его, потому что скорость не важна (хотя нагрузка на БД может быть)?
РЕДАКТИРОВАТЬ:
Я реализовал это и добавил пару триггеров на источник, чтобы вычислять представление при каждом его изменении. Чтение может быть на 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)