Когда требуется денормализация базы данных и хранение одних и тех же данных в разных таблицах или коллекциях
Что проверяют
Умеете ли вы осознанно платить целостностью за скорость чтения и понимаете ли, кто будет поддерживать копии в актуальном виде. Следом почти всегда спрашивают: как вы обновите дублированное поле, если исходное значение изменилось, и что будет, если обновление упадёт посередине.
Как отвечать
Начните с того, что денормализация не первый шаг. Сначала индексы и EXPLAIN ANALYZE в PostgreSQL, потом кеш в Redis, и только если горячий запрос всё равно упирается в JOIN или агрегацию, данные дублируют. Типичные поводы:
- чтение сильно преобладает над записью: ленту или карточку товара открывают тысячи раз, а редактируют редко;
- счётчики и агрегаты:
likes_countилиorders_totalв строке вместоCOUNT(*)на каждый запрос; - модель документа в MongoDB: данные, которые читаются вместе, кладут в один документ, потому что
$lookupдорог. Выбор между встраиванием и ссылкой делают по паттерну доступа, помня про лимит документа в 16 МБ; - отчёты и аналитика: отдельная витрина или материализованное представление.
Важно разделить два случая. Цена товара и адрес доставки в заказе это не денормализация, а снимок на момент события: они и не должны меняться вслед за каталогом. Настоящая денормализация создаёт копию, которая обязана совпадать с оригиналом.
Обязательно назовите механизм синхронизации: транзакция, которая меняет оригинал и копии вместе (в MongoDB мультидокументные транзакции требуют replica set), триггер в базе, REFRESH MATERIALIZED VIEW CONCURRENTLY по расписанию или событие через outbox и фоновый обработчик в очереди вроде BullMQ. Последний вариант даёт согласованность в конечном счёте, и это нужно проговорить вслух.
Где спотыкаются
Денормализуют без замеров. Без плана запроса и цифр по латентности это преждевременная оптимизация.
Не отвечают на вопрос «кто обновит копию». Дублировать user_name в заказах легко, а через год в отчётах живут три разных имени одного клиента.
Встраивают в документ MongoDB массив, который растёт без ограничений, например все комментарии к посту. Документ упирается в лимит, а обновления и чтение с каждым месяцем становятся тяжелее.