Три ответа на один вопрос
Любая витрина данных существует ради одного: ответить на бизнес-вопрос быстро и без двусмысленности. Но дорога к этому ответу бывает разной. 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: измерять и описывать
Размерное моделирование Кимбалла стоит на одном наблюдении: данные играют ровно две роли. Они либо измеряют (числа, которые суммируют и усредняют), либо описывают (контекст, по которому фильтруют и группируют). Измерения попадают в
Grain — решение, которое нельзя починить запросом
Самое ответственное решение во всём размерном дизайне — это
Правило выбора: берите самый низкий, атомарный grain — самое мелкое событие из доступных. Из атомарных строк можно собрать любой агрегат суммированием. Обратно — нельзя: если вы при загрузке свернули данные до «выручка по магазину за день», детализация по товару потеряна навсегда, и никакой запрос её не вернёт. Атомарный grain — это сохранённая возможность ответить на ещё не заданные вопросы.
Из объявленного grain вытекает всё остальное. Dimension допустима, только если у неё ровно одно значение на строку grain. Measure допустима, только если измерима на этом уровне. Самая коварная ошибка — смешанный grain (mixed grain), когда в одной fact-таблице лежат строки разной гранулярности (например, позиции чека и итоги по магазину). Запрос не падает, он возвращает правдоподобное, но завышенное число, и ошибка тихо отравляет каждую агрегацию.
SCD: что делать, когда описание меняется
Клиент переехал в другой город, у товара сменилась категория. Как dimension хранит изменения описательных атрибутов — это
- 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-методология, где обычная сущность разбивается на три структуры с разделёнными ролями.
hubHub — хранит business keys одной сущности: hash key, сам business key, дату загрузки, источник. Никаких описательных атрибутов. хранит business keys одной сущности — устойчивые идентификаторы из реального мира (номер клиента, артикул). Только hash key, business key, дата загрузки, источник. Никаких описательных атрибутов. Отвечает на вопрос «какие сущности этого типа существуют».linkLink — хранит связь между business keys, всегда трактуемую как many-to-many: собственный hash key, hash-ключи hubs, дата загрузки, источник. хранит связь между business keys, всегда как many-to-many. Собственный hash key, hash-ключи связываемых hubs, дата загрузки, источник. Тоже без описательных атрибутов. Отвечает на «что с чем связано».satelliteSatellite — хранит описательные атрибуты hub или link и всю их историю во времени. Каждое изменение атрибутов — новая строка. хранит описательные атрибуты 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 — переворачивают эту экономику, и под них появляется третья форма:
Почему денормализация вдруг выигрывает? Три причины, все про физику колоночного хранения.
Во-первых, колонки читаются независимо. Аналитический запрос трогает 5 столбцов из 200 — движок читает с диска только эти 5, остальные 195 не стоят почти ничего. Лишняя ширина таблицы не наказывается так, как в строковой базе, где строка читается целиком.
Во-вторых, столбец из повторяющихся значений сжимается почти бесплатно. Денормализация дублирует city и category в каждой строке факта — в строковой базе это раздуло бы хранилище, но колоночное сжатие (RLE, словарное кодирование) превращает миллион повторов "Москва" в горстку байт. Избыточность, которую нормализация боролась устранить, здесь почти не стоит места.
В-третьих, JOIN на больших данных — дорогая операция. Соединение миллиардной fact-таблицы с измерениями требует распределённого shuffle или построения hash-таблиц в памяти. Денормализация платит этой ценой один раз на этапе загрузки (ETL), а не на каждом из тысяч запросов чтения. Запрос к OBT — это последовательное сканирование нескольких колонок с агрегацией, идеальный паттерн для векторизованного движка.
Платой за OBT становится управление историчностью и дублированием при записи: логику SCD приходится разворачивать в ETL, а изменение атрибута измерения переписывает множество строк факта. Поэтому OBT — это форма gold-слоя для конкретных, хорошо понятых паттернов потребления, а не замена нормализованному ядру.
Сводная таблица
| Критерий | Star schema | Data Vault | One 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, где моделирование стыкуется с движками, на которых эти витрины живут.