Схемы: правила игры для данных
Схема — это контракт, как данные устроены: какие поля, какие типы, какие обязательны. Без схемы данные — хаос, никто не знает, что в них.
Но когда мы применяем схему — это фундаментальный вопрос в DE. Есть два подхода: schema-on-write и schema-on-read. От выбора зависит вся архитектура.
Что такое схема
Простой пример. Таблица orders:
CREATE TABLE orders (
order_id BIGINT NOT NULL PRIMARY KEY,
customer_id BIGINT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status VARCHAR(20) NOT NULL,
created_at TIMESTAMP NOT NULL DEFAULT now()
);
Здесь схема — это:
- Структура: 5 колонок.
- Типы: BIGINT, DECIMAL, VARCHAR, TIMESTAMP.
- Constraints: NOT NULL, PRIMARY KEY, DEFAULT.
Если кто-то попытается записать amount = 'привет' или строку без customer_id, БД отклонит запись. Это и есть контракт.
Schema-on-write vs Schema-on-read
Schema-on-write
«Сначала определи схему, потом пиши».
- Postgres, MySQL, Snowflake, BigQuery — все DWH работают так.
- При INSERT БД проверяет: тип правильный? NOT NULL поле не пустое? Constraints соблюдены?
- Если нет — INSERT не проходит.
Плюсы:
- Данные всегда валидны.
- Аналитика работает: знаешь, что в колонке
amountвсегда число. - Performance: индексы и колоночное хранение работают.
Минусы:
- Schema change — боль. Добавить колонку = ALTER TABLE, в большом DWH это часы.
- Не для эволюционирующих источников: API меняется, схема устаревает.
Schema-on-read
«Пиши что есть, разберись потом».
- Data Lake (S3), NoSQL (MongoDB), JSON-логи — все так.
- Пишем JSON / файлы в любом формате.
- При чтении (через SQL поверх Lake, например Athena / Snowflake EXTERNAL TABLES) применяем схему: «считай это JSON, поле
user_id— INT».
Плюсы:
- Гибкость — пиши что угодно.
- Источник может менять схему — твой Lake это переживёт.
- Cheap — S3 дёшев.
Минусы:
- Data swamp — никто не знает, какие данные актуальны, как использовать.
- Производительность ниже — парсинг при каждом чтении.
- Без discipline — хаос.
Modern Data Stack: гибрид
Современный подход — гибрид:
Идея:
- Сырое — пиши как есть (schema-on-read). Если API изменится, ты ничего не теряешь.
- На уровне staging — извлеки нужные поля, типизируй, дай схему.
- На уровне marts — строгая схема для аналитики и BI.
Это даёт гибкость на входе и дисциплину на выходе.
Schema evolution
Источники меняются. Salesforce добавил поле, инженер добавил колонку в Postgres, маркетинг переименовал тег. Что делает твой pipeline?
Сценарии
Backward / Forward compatibility
В мире streaming (Kafka) важны два понятия:
- Backward compatible — новые консьюмеры читают старые сообщения. Можно добавить optional поле.
- Forward compatible — старые консьюмеры читают новые сообщения. Игнорируют новые поля.
Avro и Protobuf — оба supports эти концепции через schema rules.
Schema Registry
В streaming-мире (Kafka) есть отдельная сущность: Schema Registry. Это сервер, который хранит версии схем.
Что даёт:
- Версионирование схем — знаем, какая версия использовалась.
- Compatibility checks — registry отклоняет breaking changes автоматически.
- Эффективность — не нужно слать схему в каждом сообщении (Avro фишка).
Confluent Schema Registry — главный игрок. AWS Glue Schema Registry — конкурент. Apicurio — open-source.
Основы schema evolution: backward, forward, full compatibilitySchema Tests в dbt
В MDS схема валидируется тестами в dbt:
# models/schema.yml
version: 2
models:
- name: stg_orders
columns:
- name: order_id
tests:
- not_null
- unique
- name: amount
tests:
- not_null
- dbt_utils.expression_is_true:
expression: ">= 0"
- name: status
tests:
- accepted_values:
values: ['pending', 'completed', 'cancelled']
Что это делает:
- При
dbt testзапускает проверки на каждую колонку. - Если null там, где
not_null— тест падает, алерт. - Если дубликаты в
unique— тест падает. - Если новое status появилось — тест падает, ты узнаёшь сразу.
Это твоя страховка от schema changes в источнике.
Data Contracts — современный тренд
Современная идея: data contract между производителем данных и потребителем.
Идея:
- Producer (бэкенд-команда) обязан публиковать схему и SLA до того, как DE строит pipeline.
- Breaking changes (rename, drop column) — только с согласия consumers.
- Контракт версионируется в Git.
В 2024-2026 это активно растущий подход (Andrew Jones, Chad Sanderson). Инструменты: dbt contracts, GreatExpectations contracts, Datacontract-cli.
Зачем управлять схемами: Schema Registry в KafkaРеальный пример эволюции схемы
Стартап, 2 года жизни:
Год 1 (MVP):
-- Postgres
CREATE TABLE users (id INT, email VARCHAR, created_at TIMESTAMP);
Год 2 (рост):
-- Postgres: добавили поля
ALTER TABLE users ADD COLUMN name VARCHAR;
ALTER TABLE users ADD COLUMN phone VARCHAR;
ALTER TABLE users ADD COLUMN deleted_at TIMESTAMP; -- soft delete
DE-pipeline:
- Bronze (raw.users) — JSON через Fivetran, ловит новые поля автоматически.
- Silver (stg_users) — нужно добавить новые колонки и retest.
- Gold (mart.user_360) — обновить, чтобы использовать
deleted_at.
Срыв:
В год 2 backend решил переименовать email -> email_address. Не предупредил.
Что произошло:
- Fivetran подтянул новую колонку
email_address. - Старая колонка
emailстала NULL для новых записей. - В Silver schema-test
not_null on emailупал. Алерт сработал. - DE поговорил с backend, добавил миграцию: COALESCE(email, email_address).
Без теста это попало бы в Gold, и аналитики получили бы пустые email. С тестом — поймали на staging.
Это и есть data contract в действии.
Что выбрать новичку
Базовое правило:
- Сырое в Lake / Bronze — schema-on-read (просто JSON, без типов).
- Staging / Silver — strict schema через dbt models + tests.
- Mart / Gold — strict schema, дополнительные constraints.
И обязательно тесты на каждый слой. Без тестов schema changes тихо ломают пайплайн.
Попробуй сам
-
Возьми любой публичный API (например, GitHub API на
https://api.github.com/users/torvalds). Получи JSON. Подумай: какую schema-on-write ты бы спроектировал для DWH? Какие поля обязательны, какие — опциональны? -
Подумай: твоя любимая компания (например, банк, e-commerce). Какие могут быть breaking changes в схеме их данных, которые тихо сломают аналитику? Например, переименование статуса заказа.