Что такое денормализация в базах данных?

Ответ

Денормализация — это сознательное отступление от нормальных форм реляционной базы данных путем введения контролируемой избыточности данных. Цель — оптимизировать скорость выполнения операций чтения за счет увеличения объема данных и усложнения операций обновления.

Типичный сценарий из моего опыта: В системе отчетов для агрегации данных требовались сложные JOIN между таблицами Orders, Customers и Products. Это замедляло генерацию ежедневных дашбордов.

До (нормализованная схема):

CREATE TABLE Orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    product_id INT,
    quantity INT,
    FOREIGN KEY (customer_id) REFERENCES Customers(customer_id),
    FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
-- Для отчета нужен JOIN:
SELECT o.order_id, c.name, p.product_name, o.quantity
FROM Orders o
JOIN Customers c ON o.customer_id = c.customer_id
JOIN Products p ON o.product_id = p.product_id;

После (денормализованная схема):

CREATE TABLE Denormalized_Orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    customer_name VARCHAR(100), -- Данные из Customers
    product_id INT,
    product_name VARCHAR(100),  -- Данные из Products
    product_price DECIMAL(10,2), -- Еще данные из Products
    quantity INT,
    total_price DECIMAL(10,2) AS (product_price * quantity) -- Вычисляемое поле
);
-- Теперь отчет формируется без JOIN:
SELECT order_id, customer_name, product_name, quantity, total_price
FROM Denormalized_Orders;

Плюсы:

  • Резкое ускорение SELECT-запросов за счет исключения дорогостоящих операций JOIN.
  • Упрощение схемы БД для систем аналитики (OLAP) и чтения.

Минусы и риски:

  • Аномалии обновления: Изменение имени продукта в таблице Products потребует обновления всех связанных строк в Denormalized_Orders.
  • Увеличение занимаемого места.
  • Усложнение логики обновления: Требуются триггеры или процедуры для поддержания согласованности.

Когда это оправдано: В read-heavy системах (отчеты, аналитика, кэши), где производительность чтения критична, а данные обновляются редко (например, через ETL-процессы ночью).

Ответ 18+ 🔞

Ну ты представляешь, эта денормализация — это как будто ты такой: «А похуй на все правила, давайте нахуярим данных куда попало!». По сути, это сознательный пиздец нормальным формам базы данных. Мы специально плодим одни и те же данные в разных местах, чтобы потом не ебаться с этими бесконечными JOIN'ами. Цель одна — чтобы SELECT'ы летали как угорелые, а то уже терпения ноль, ебать.

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

Было (когда мы ещё верили в светлое будущее нормальных форм):

CREATE TABLE Orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    product_id INT,
    quantity INT,
    FOREIGN KEY (customer_id) REFERENCES Customers(customer_id),
    FOREIGN KEY (product_id) REFERENCES Products(product_id)
);
-- А чтобы отчёт получить, надо было вот это вот ебало:
SELECT o.order_id, c.name, p.product_name, o.quantity
FROM Orders o
JOIN Customers c ON o.customer_id = c.customer_id
JOIN Products p ON o.product_id = p.product_id;

Стало (когда мы сказали «ёпта, надоело» и пошли на принцип):

CREATE TABLE Denormalized_Orders (
    order_id INT PRIMARY KEY,
    customer_id INT,
    customer_name VARCHAR(100), -- Просто тупо скопировали из Customers
    product_id INT,
    product_name VARCHAR(100),  -- И из Products тоже
    product_price DECIMAL(10,2), -- Да хуй с ним, заодно и цену сунем
    quantity INT,
    total_price DECIMAL(10,2) AS (product_price * quantity) -- И посчитаем сразу, чё уж там
);
-- И теперь отчёт — это просто взять и выгрести:
SELECT order_id, customer_name, product_name, quantity, total_price
FROM Denormalized_Orders;

Что хорошего, ёпта:

  • SELECT'ы просто взлетают. JOIN'ов нет — и волнение, ебать, прошло. Всё мгновенно.
  • Схема для аналитиков становится проще. Чувак открывает таблицу и сразу всё видит, не надо быть гением SQL.

Но подводных ебушки-воробушки — хоть завались:

  • Аномалии обновления — это пиздец. Захотел поменять название товара в основной таблице? Поздравляю, теперь тебе надо этот же пиздец обновить в каждой строке заказа, где этот товар встречается. Один раз забудешь — и в отчёте уже херня.
  • Места жрёт овердохуища. Данные дублируются — значит, и дисков больше надо.
  • Логика обновления превращается в ад. Нужны триггеры или какие-то процедуры, которые будут синхронизировать эту всю хуйту. Доверия к ним, ебать, ноль, но делать надо.

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