Перейти к содержанию
Learning Platform

Три ответа на один вопрос

Любая витрина данных существует ради одного: ответить на бизнес-вопрос быстро и без двусмысленности. Но дорога к этому ответу бывает разной. Star schema, Data Vault и One Big Table — это не три моды и не три поколения, которые сменяют друг друга. Это три ответа на три разных вопроса, которые часто живут в одном хранилище одновременно, на разных слоях.

Чтобы не запутаться, держите в голове одну рамку. У хранилища обычно три слоя: сырьё из источников (bronze), интегрированное ядро (silver) и слой потребления, к которому ходят аналитики и BI (gold). Star schema — это форма gold-слоя. Data Vault — это форма silver-ядра. One Big Table — это альтернативная форма gold для колоночных движков. Каждый подход оптимизирует под свою задачу, и спор «что лучше» почти всегда оказывается спором о слое, на котором их сравнивают. Разберём каждый до уровня того, что физически лежит в таблице и почему.

Это бесплатный самостоятельный разбор. Он пересекается с модулями нашего курса Data Modeling с запускаемым SQL-sandbox, но читается отдельно и с нуля.

Dimensional modeling: измерять и описывать

Размерное моделирование Кимбалла стоит на одном наблюдении: данные играют ровно две роли. Они либо измеряют (числа, которые суммируют и усредняют), либо описывают (контекст, по которому фильтруют и группируют). Измерения попадают в fact table, описания — в dimension table. Из этого разделения вырастает форма звезды: факт в центре, измерения вокруг, ровно один JOIN между фактом и каждым измерением.

Grain — решение, которое нельзя починить запросом

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

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

Из объявленного grain вытекает всё остальное. Dimension допустима, только если у неё ровно одно значение на строку grain. Measure допустима, только если измерима на этом уровне. Самая коварная ошибка — смешанный grain (mixed grain), когда в одной fact-таблице лежат строки разной гранулярности (например, позиции чека и итоги по магазину). Запрос не падает, он возвращает правдоподобное, но завышенное число, и ошибка тихо отравляет каждую агрегацию.

SCD: что делать, когда описание меняется

Клиент переехал в другой город, у товара сменилась категория. Как dimension хранит изменения описательных атрибутов — это SCD. Три базовых типа:

  • Type 1 — перезапись. Старое значение затирается новым. Истории нет: на вопрос «в каком городе клиент был на момент покупки» ответить невозможно. Просто и дёшево, годится там, где прошлое значение никому не нужно (исправление опечатки).
  • Type 2 — новая строка. На каждое изменение заводится новая версия строки с тем же business key, но новым суррогатным ключом, парой valid_from / valid_to и флагом is_current. Старые факты продолжают ссылаться на старую версию — это и есть историчность. Самый частый и самый важный тип.
  • Type 3 — отдельный столбец. Хранит только предыдущее значение в колонке previous_city рядом с current_city. Видна ровно одна ступенька истории. Редкий, узкоспециальный приём.

Type 2 — рабочая лошадь. Именно он позволяет считать факты в контексте, который был актуален на момент события, а не на сегодня.

Star vs Snowflake

Snowflake-схема — это star, у которой измерения нормализованы: вместо одной широкой dim_product появляются dim_product, dim_category, dim_brand, связанные внешними ключами. Экономия места и устранение избыточности — ценой того, что запрос к товару теперь требует каскада JOIN по цепочке нормализованных таблиц.

Для OLAP это плохой обмен. Dimension-таблицы малы по сравнению с фактом (тысячи строк против миллиардов), поэтому экономия места ничтожна, а каждый лишний JOIN на gold-слое замедляет запросы и усложняет модель для аналитика. Поэтому каноничный Кимбалл — это денормализованная star, а snowflake применяют точечно: когда у измерения очень большая редко используемая иерархия или когда часть измерения переиспользуется как conformed dimension в нескольких звёздах.

Data Vault: backbone для аудита и историчности

Star отлично работает как слой потребления, но плохо как интегрированное ядро для сырых данных из десятка источников. Тут проблема не в скорости запросов, а в управляемости изменений: новый источник, новое бизнес-правило, требование «докажи, что это значение пришло именно оттуда и именно тогда». На это отвечает Data Vault — silver-методология, где обычная сущность разбивается на три структуры с разделёнными ролями.

  • hub хранит business keys одной сущности — устойчивые идентификаторы из реального мира (номер клиента, артикул). Только hash key, business key, дата загрузки, источник. Никаких описательных атрибутов. Отвечает на вопрос «какие сущности этого типа существуют».
  • link хранит связь между business keys, всегда как many-to-many. Собственный hash key, hash-ключи связываемых hubs, дата загрузки, источник. Тоже без описательных атрибутов. Отвечает на «что с чем связано».
  • satellite хранит описательные атрибуты hub или link и всю их историю. Каждое изменение — новая строка. Отвечает на «какими были атрибуты и как менялись».

Зачем такое дробление? Ради трёх вещей. Auditability: ничего не удаляется и не перезаписывается, только добавляется, и всегда можно доказать что, когда и откуда попало в хранилище. Гибкость: новый источник или сущность — это новые таблицы, старые не трогаются, схема растёт добавлением, а не миграцией. Параллельная загрузка: ключи — это hash (MD5/SHA-256) от business key, а не sequence. Хеш детерминирован, любой процесс вычисляет его независимо, поэтому hub, link и satellite грузятся параллельно без центрального координатора, выдающего номера.

-- Тот же клиент, разнесённый по трём структурам Data Vault
-- HUB: только бизнес-ключ и метаданные происхождения
CREATE TABLE hub_customer (
  customer_hk   CHAR(32) PRIMARY KEY,   -- MD5(customer_id)
  customer_id   VARCHAR  NOT NULL,      -- business key из источника
  load_dts      TIMESTAMP NOT NULL,
  record_source VARCHAR  NOT NULL
);

-- SATELLITE: описательные атрибуты + вся история (каждое изменение = новая строка)
CREATE TABLE sat_customer_details (
  customer_hk   CHAR(32) NOT NULL,
  load_dts      TIMESTAMP NOT NULL,     -- вместе с hk образует PK
  city          VARCHAR,
  email         VARCHAR,
  hash_diff     CHAR(32) NOT NULL,      -- хеш атрибутов: менять строку или нет
  record_source VARCHAR  NOT NULL,
  PRIMARY KEY (customer_hk, load_dts)
);

Цена аудита и гибкости — нечитаемость для людей. Чтобы собрать «клиента» обратно, нужен JOIN hub плюс satellite плюс link плюс соседние hubs. Поэтому Data Vault не предназначен для прямого потребления аналитиками: его роль — auditable, гибкий, параллельно загружаемый backbone между источниками и витринами. Мейнстрим-связка: Data Vault как silver-ядро, а gold-витрины поверх него строят размерными, по Кимбаллу.

One Big Table: денормализация под колоночные движки

И star, и Data Vault выросли в эпоху строковых баз, где JOIN дёшев, а место дорого. Колоночные движки — ClickHouse, BigQuery, Snowflake — переворачивают эту экономику, и под них появляется третья форма: OBT (OBT, wide table) — широкая плоская таблица, где факт и все нужные атрибуты измерений уже сведены вместе, без JOIN на чтении.

Почему денормализация вдруг выигрывает? Три причины, все про физику колоночного хранения.

Во-первых, колонки читаются независимо. Аналитический запрос трогает 5 столбцов из 200 — движок читает с диска только эти 5, остальные 195 не стоят почти ничего. Лишняя ширина таблицы не наказывается так, как в строковой базе, где строка читается целиком.

Во-вторых, столбец из повторяющихся значений сжимается почти бесплатно. Денормализация дублирует city и category в каждой строке факта — в строковой базе это раздуло бы хранилище, но колоночное сжатие (RLE, словарное кодирование) превращает миллион повторов "Москва" в горстку байт. Избыточность, которую нормализация боролась устранить, здесь почти не стоит места.

В-третьих, JOIN на больших данных — дорогая операция. Соединение миллиардной fact-таблицы с измерениями требует распределённого shuffle или построения hash-таблиц в памяти. Денормализация платит этой ценой один раз на этапе загрузки (ETL), а не на каждом из тысяч запросов чтения. Запрос к OBT — это последовательное сканирование нескольких колонок с агрегацией, идеальный паттерн для векторизованного движка.

Платой за OBT становится управление историчностью и дублированием при записи: логику SCD приходится разворачивать в ETL, а изменение атрибута измерения переписывает множество строк факта. Поэтому OBT — это форма gold-слоя для конкретных, хорошо понятых паттернов потребления, а не замена нормализованному ядру.

Сводная таблица

КритерийStar schemaData VaultOne Big Table
Слойgold (потребление)silver (интеграция)gold (потребление)
Структурафакт + денорм. измеренияhub / link / satelliteодна широкая плоская таблица
Нормализациянизкаявысокая (3NF-подобная)отсутствует
Главная цельудобство и скорость BIаудит, историчность, гибкостьмаксимум скорости на OLAP-движке
JOIN на чтенииодин на измерениемногонет
Историчностьчерез SCD Type 2встроена в satelliteвручную в ETL
Целевой движоклюбой DWHлюбой DWH (ядро)колоночный (ClickHouse, BigQuery)
Читаемость для человекавысокаянизкаявысокая
Где больноинтеграция многих источниковпрямые запросы аналитиковизменение атрибутов, история

Как выбирать на практике

Не выбирайте один подход на всё хранилище — выбирайте под слой и под нагрузку.

  • Нужна витрина для BI на классическом DWH — star schema. Атомарный grain, conformed dimensions, SCD Type 2 там, где важна история.
  • Интегрируете данные из многих источников, нужен аудит и устойчивость к изменениям — Data Vault как silver-ядро, а сверху всё равно стройте размерные витрины для потребления.
  • Конкретный дашборд на колоночном движке, паттерн запросов известен и стабилен — денормализуйте в One Big Table и дайте движку сканировать.

Канонический зрелый стек выглядит так: сырьё в bronze, Data Vault как auditable backbone в silver, а в gold — star-витрины для гибкого BI плюс точечные OBT под тяжёлые предсказуемые дашборды. Это не компромисс, а разделение труда: каждая форма стоит там, где её сильная сторона совпадает с задачей слоя.

Что дальше

Если хотите не прочитать, а прощупать руками — спроектировать grain, развернуть SCD Type 2, собрать звезду и сравнить её план запроса с денормализованной таблицей в запускаемом SQL-sandbox, — это и есть содержание нашего бесплатного курса Data Modeling. Он входит в направление Data Engineering, где моделирование стыкуется с движками, на которых эти витрины живут.

Ещё в направлении · Data Engineering

Все материалы направления →