Денормализация и дублирование
Почему в документной БД дублировать данные иногда правильно — и какова за это плата.
Денормализация — осознанное дублирование данных ради того, чтобы частые запросы обходились без объединения коллекций.
В SQL дублирование — грех, в Mongo — инструмент
Реляционная нормализация борется с дублированием: каждый факт хранится в одном месте. Цена — JOIN при каждом чтении. В документной модели чтения часто важнее, поэтому небольшое дублирование данных, которые редко меняются, — допустимый и распространённый приём.
Пример: имя автора в посте
У поста есть автор. Можно хранить только authorId и подтягивать имя отдельным запросом. Но лента постов показывает имя автора всегда — значит, выгодно продублировать его прямо в посте:
{
"_id": "p1",
"title": "Заметки о Mongo",
"authorId": "u1",
"authorName": "Анна"
}Теперь лента рисуется одним запросом к posts, без обращения к users. На больших объёмах это огромная экономия.
Плата: рассинхрон при изменении
У дублирования есть обратная сторона. Если пользователь сменит имя, продублированное authorName в постах не обновится само — придётся пройтись по постам:
db.posts.updateMany(
{ authorId: "u1" },
{ $set: { authorName: "Анна Петрова" } }
)Поэтому денормализуют именно редко меняющиеся данные. Имя пользователя меняется раз в годы — дублировать безопасно. Баланс на счёте меняется постоянно — дублировать нельзя.
Когда денормализация оправдана
- Чтений намного больше, чем записей. Ленты, каталоги, карточки — оптимизируем под чтение.
- Дублируемые данные стабильны. Имя, заголовок, категория — а не цена и остаток.
- JOIN на каждом чтении дорог. Денормализация убирает
$lookupиз горячего пути.
Когда не стоит
Если данные меняются часто или должны быть всегда абсолютно точны (финансы, остатки склада в реальном времени), дублирование создаёт риск рассогласования. Тогда лучше ссылка и явный $lookup. И главное — не дублируйте «на всякий случай»: денормализация оправдана конкретным частым запросом, а не абстрактным «вдруг пригодится».
Итог
- Денормализация ускоряет чтение, убирая необходимость объединять коллекции.
- Плата — ручная синхронизация дубля при изменении исходных данных.
- Дублируйте редко меняющиеся данные и только под реальный частый запрос.