Learning Platform
Глоссарий Troubleshooting
Урок 03.01 · 18 мин
Начальный
data-modelinganomaliesdata-as-asset

Зачем моделировать данные: цена плохой модели

В прошлом модуле мы договорились, что модель данных — это чертёж между реальностью и базой. Теперь разберём этот тезис строго: что конкретно происходит без чертежа, во что обходится плохая модель и почему усилия на моделирование окупаются. Это не философия — это про реальные баги, медленные запросы и сорванные сроки, с которыми сталкивается любая команда.

Моделирование как инвестиция

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

Цена изменения схемы растёт со временем нелинейно. Поправить чертёж на этапе проектирования — минуты. Изменить структуру таблицы, когда приложение уже написано, но данных мало — часы плюс правки кода. Перестроить схему, когда в базе миллионы строк и десяток сервисов от неё зависят — это многонедельный проект с миграцией данных, риском простоя и потери данных.

Как растёт цена изменения схемы
Этап проектированияСхема ещё на бумаге или в редакторе диаграмм. Изменить — минуты, правок кода нет, данных нет.
время идёт
Код написан, данных малоПриложение уже использует схему. Изменение требует правок кода и миграции, но данных немного. Часы-дни.
время идёт
Прод: миллионы строкВ базе много данных, десяток сервисов зависит от схемы. Изменение — проект с миграцией, риском простоя и потери данных. Недели-месяцы.

Это и есть экономический аргумент за моделирование: вы оплачиваете дешёвую работу сейчас, чтобы не оплачивать дорогую потом.

Что даёт хорошая модель: четыре свойства

Хорошая модель данных обеспечивает четыре конкретных свойства. Когда говорят «модель плохая», обычно нарушено одно из них.

Целостность (integrity). Данные не противоречат друг другу и правилам предметной области. У товара одна цена, а не три разных. Заказ не может ссылаться на несуществующего клиента. Количество товара не отрицательное. Хорошая модель делает невозможные состояния невозможными — база физически не даёт записать противоречие.

Отсутствие избыточности. Каждый факт хранится ровно один раз. Email клиента — в одном месте. Это не про экономию диска (диск дёшев) — это про то, что один экземпляр факта невозможно рассинхронизировать сам с собой.

Производительность нужных запросов. Модель проектируется под те запросы, которые система будет делать. Для OLTP это быстрая запись и точечное чтение, для OLAP — быстрые агрегации. Запросы, которые система делает часто, должны быть дёшевы.

Расширяемость. Систему можно развивать, не ломая существующее. Добавить скидки, новый тип оплаты, ещё одну страну — без переписывания половины базы.

СвойствоЧто нарушается без негоПример сбоя
ЦелостностьДанные противоречивыЗаказ ссылается на удалённого клиента
Нет избыточностиДубли рассинхронизируютсяДва разных email у одного человека
ПроизводительностьЗапросы медленныеОтчёт строится 4 минуты
РасширяемостьИзменение ломает староеНовое поле требует переписать 20 запросов

Цена плохой модели: аномалии вблизи

Главная конкретная цена плохой модели — аномалии. В прошлом модуле мы их назвали, теперь посмотрим вплотную на SQL. Возьмём плоскую таблицу, где смешаны данные о сотрудниках и отделах:

CREATE TABLE staff (
    emp_id      INTEGER,
    emp_name    TEXT,
    dept_name   TEXT,
    dept_budget INTEGER
);

INSERT INTO staff VALUES
    (1, 'Анна',   'Маркетинг', 500000),
    (2, 'Борис',  'Маркетинг', 500000),
    (3, 'Виктор', 'Финансы',   800000);

Бюджет отдела «Маркетинг» записан дважды — в строках Анны и Бориса. Это избыточность, и она порождает три аномалии.

Аномалия обновления. Бюджет маркетинга вырос до 600000. Корректный апдейт:

UPDATE staff SET dept_budget = 600000 WHERE dept_name = 'Маркетинг';

Если же кто-то по ошибке обновит только одну строку:

UPDATE staff SET dept_budget = 600000 WHERE emp_id = 1;
-- теперь у отдела Маркетинг два разных бюджета: 600000 и 500000

База приняла этот UPDATE без возражений — она не знает, что бюджет отдела должен быть один. Данные стали противоречивыми, и никакой запрос больше не даст достоверного ответа «каков бюджет маркетинга».

Аномалия вставки. Открыли отдел «Логистика» с бюджетом, но сотрудников туда ещё не наняли. Куда записать отдел? Строка таблицы — это сотрудник:

INSERT INTO staff VALUES (NULL, NULL, 'Логистика', 300000);
-- приходится ставить NULL в emp_id и emp_name — строка-сотрудник без сотрудника

Получается строка с пустым сотрудником. Это не запись об отделе — это испорченная запись о сотруднике. Факт «существует отдел Логистика» негде хранить чисто.

Аномалия удаления. Виктор — единственный в отделе «Финансы». Виктор уволился:

DELETE FROM staff WHERE emp_id = 3;
-- вместе с Виктором исчез сам факт существования отдела Финансы и его бюджета

Удалив сотрудника, мы потеряли несвязанный факт — отдел и его бюджет. База опять не возразила.

WARNING

Обратите внимание: во всех трёх случаях СУБД выполнила команду без ошибки. Плохая модель не «ломается» с сообщением об ошибке — она тихо допускает порчу данных. Ошибку обнаруживают потом, когда отчёт показывает чушь, и долго ищут причину. Хорошая модель, наоборот, не дала бы выполнить противоречивую операцию.

Решение — разнести данные по двум таблицам: departments (отдел и бюджет, каждый отдел один раз) и employees (сотрудник со ссылкой на отдел). Тогда бюджет хранится в одном месте, отдел заводится независимо от сотрудников, а увольнение последнего сотрудника не трогает запись об отделе. Как делать это системно — тема модуля про нормализацию.

Нормализация в PostgreSQL: 1NF, 2NF, 3NF

Аномалия — это нарушение инварианта

Поднимемся на уровень выше. Инвариант — это правило предметной области, которое должно соблюдаться всегда: «у отдела ровно один бюджет», «у заказа ровно один клиент». Хорошая модель кодирует инварианты в саму структуру так, что нарушить их невозможно.

В плохой схеме инвариант «у отдела один бюджет» не закодирован — он существует только в голове разработчика. Поэтому база не может его защитить. Аномалия — это и есть момент, когда инвариант нарушился, а база не имела средств этому помешать.

Где живёт инвариант — и кто его защищает
Плохая схемаПравило 'у отдела один бюджет' нигде в структуре не выражено. Существует только как договорённость в голове разработчика.
база не знает правила
Аномалия возможнаСУБД выполняет противоречивую операцию без ошибки, потому что не знает об инварианте. Данные портятся тихо.

Вот почему нормализация — это не про «красоту» схемы. Это про корректность: правильная декомпозиция превращает инвариант в структурное свойство, и СУБД начинает его охранять автоматически.

Данные как актив: ещё один угол

Мы говорили, что данные — актив, чья ценность зависит от структуры. Добавим практический угол: на данных принимают решения. Руководитель смотрит отчёт о выручке и решает, какой товар закупать. Если в основе отчёта плохая модель с рассинхронизированными дублями, отчёт врёт — и решение принимается по ложным данным.

Стоимость плохой модели здесь — не «медленный запрос», а неправильное бизнес-решение. Это переводит моделирование из разряда «технические детали для разработчиков» в разряд «то, от чего зависит, можно ли доверять данным компании вообще».

NOTE

Есть и обратная сторона: иногда избыточность добавляют сознательно — это денормализация. В аналитических хранилищах продублировать атрибут ради скорости чтения — нормальное инженерное решение. Разница в том, что денормализация делается осознанно, с пониманием цены, и под контролем — а не случайно. Этому посвящён отдельный урок модуля про нормализацию.

Хорошая модель и скорость разработки

Есть ещё одно следствие хорошей модели, которое часто недооценивают: она ускоряет не только запросы, но и саму разработку. Когда структура данных продумана, новые функции приложения ложатся на неё естественно. Нужно добавить отзывы к товарам — заводится таблица reviews со ссылкой на товар и клиента, и всё. Существующий код не трогается.

В плохо смоделированной базе всё иначе. Каждая новая функция — это борьба со структурой: данные лежат не там, где нужно, приходится их дублировать, писать обходные запросы, добавлять костыли. Разработка замедляется, и чем дальше, тем сильнее. Команды называют это «технический долг в данных» — и проценты по нему платятся каждым спринтом.

Шесть измерений качества данных

Поэтому моделирование — это вложение не только в корректность, но и в скорость команды. Хорошая модель — это фундамент, на котором быстро строится. Плохая — это болото, в котором каждый шаг даётся с усилием.

TIP

Признак того, что модель начала мешать: простые на первый взгляд задачи («добавить поле», «показать ещё один отчёт») регулярно оказываются неожиданно сложными. Если это происходит часто — проблема, скорее всего, не в исполнителях, а в модели данных под ними.

Попробуй сам

Создайте у себя плоскую таблицу staff из этого урока и наполните её данными. Затем своими руками воспроизведите все три аномалии: выполните «частичный» UPDATE одной строки и убедитесь запросом SELECT DISTINCT dept_name, dept_budget FROM staff, что у одного отдела появилось два бюджета; попробуйте вставить отдел без сотрудника; удалите единственного сотрудника отдела и убедитесь, что отдел исчез. После этого спроектируйте на бумаге две таблицы — departments и employees — и объясните себе словами, почему в новой схеме каждая из трёх аномалий становится невозможной. Сохраните этот разбор: это прямая подготовка к лабе по нормализации.


Проверка знанийKnowledge check
Почему говорят, что аномалия — это нарушение инварианта, и почему СУБД не может защитить от аномалии в плохо спроектированной схеме?
ОтветAnswer
Инвариант — это правило предметной области, которое должно соблюдаться всегда, например «у отдела ровно один бюджет» или «у заказа ровно один клиент». Аномалия — это момент, когда такой инвариант нарушился: например, частичный UPDATE оставил у одного отдела два разных бюджета. СУБД не может защитить от аномалии в плохой схеме, потому что в плохой схеме инвариант нигде структурно не выражен — он существует только как договорённость в голове разработчика. Когда бюджет отдела продублирован в N строках таблицы сотрудников, ничто в структуре не говорит базе, что эти значения обязаны совпадать, поэтому она спокойно выполняет противоречивую операцию без ошибки и тихо портит данные. Хорошая модель, наоборот, кодирует инвариант в саму структуру (например, выносит бюджет в отдельную таблицу departments, где у отдела ровно одна строка), и тогда СУБД охраняет правило автоматически. Поэтому нормализация — это про корректность данных, а не про «красоту» схемы.

Проверьте понимание

Результат: 0 из 0
Концептуальный
Вопрос 1 из 4. Почему урок называет моделирование данных инвестицией?

Закончили урок?

Отметьте его как пройденный, чтобы отслеживать свой прогресс

Войдите чтобы оценить урок

Прогресс модуля
0 из 5