Files
de-roadmap/dwh-modeling/DataVault.md
T

24 KiB
Raw Blame History

Data Vault 2.0: как собрать хранилище как конструктор

Для тех, кто уже слышал про STG/ODS/DDS/DM, факты/измерения и SCD, но хочет разобраться, что такое Data Vault и зачем он вообще нужен.


1. Зачем вообще нужен Data Vault?

Большинство знакомятся с хранилищами через две модели:

  • 3NF (Инмон) — нормализованное ядро: много таблиц, строгие связи, минимум дублирования.
  • Звезда (Star Schema) (Кимбалл) — витрины под отчёты: факт + несколько «плоских» измерений.

Этого хватает для:

  • 25 источников,
  • относительно стабильных схем,
  • задач типа «сделать отчёт для маркетинга/финансов».

Проблемы начинаются, когда:

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

Вот тут обычная 3NF/Звезда начинает скрипеть:

  • любое изменение источника → больно по ядру и витринам;
  • история размазана по разным местам (где-то SCD, где-то лог-таблицы, где-то вообще нет истории);
  • добавление нового источника превращается в мини-проект на месяц.

Data Vault 2.0 отвечает именно на эту боль:

Как сделать так, чтобы новый источник → это не «ремонт всего дома», а просто «докрутить ещё один модуль»?


2. Интуиция: DV как конструктор Lego

Классическая метафора DV — это конструктор из трёх типов деталей:

  • 🔴 Hub (Хаб)«кто/что это» Сущности: клиент, заказ, договор, счёт. Внутри: бизнес-ключ (customer_id, contract_number) + техполя.

  • Link (Линк)«как они связаны» «Клиент сделал заказ», «договор относится к счёту». Внутри: ссылки на хабы + техполя.

  • 🟡 Satellite (Сателлит)«какие у них свойства и как они менялись» Атрибуты сущности (имя, email, статус, тариф) + история изменений.

Главная идея:

Идентичность, связи и атрибуты живут отдельно. Тогда изменения в одном не ломают другое.

На картинке это выглядит примерно так:

erDiagram
    HUB_CUSTOMER ||--o{ SAT_CUSTOMER_INFO : "имеет атрибуты"
    HUB_CUSTOMER ||--o{ LINK_ORDER_CUSTOMER : "участвует в"
    HUB_ORDER ||--o{ LINK_ORDER_CUSTOMER : "создан"
    LINK_ORDER_CUSTOMER ||--o{ SAT_ORDER_STATUS : "имеет историю статусов"

    HUB_CUSTOMER {
        varchar customer_bk PK "Бизнес-ключ: '101'"
        varchar record_source
        timestamp load_dttm
    }
    
    HUB_ORDER {
        varchar order_id PK "Бизнес-ключ: '5001'"
        varchar record_source
        timestamp load_dttm
    }
    
    LINK_ORDER_CUSTOMER {
        varchar link_key PK "Хэш (order_id + customer_bk)"
        varchar order_id FK
        varchar customer_bk FK
        varchar record_source
        timestamp load_dttm
    }
    
    SAT_CUSTOMER_INFO {
        varchar customer_bk FK
        varchar hashdiff
        timestamp valid_from
        timestamp valid_to
        varchar name
        varchar email
        varchar city
        varchar record_source
        timestamp load_dttm
    }
    
    SAT_ORDER_STATUS {
        varchar link_key FK
        varchar hashdiff
        timestamp valid_from
        timestamp valid_to
        varchar status
        varchar record_source
        timestamp load_dttm
    }

Как это читать:

  • 🔴 HUB_CUSTOMER / HUB_ORDER — «этот клиент существует», «этот заказ существует»;
  • LINK_ORDER_CUSTOMER — «именно этот заказ сделал именно этот клиент»;
  • 🟡 SAT_… — как менялись атрибуты (email, статус и т.п.) во времени.

3. Три типа таблиц в Data Vault 2.0

Чуть менее «сказочно», чуть более технично.

3.1. Hub — сущность и её бизнес-ключ

Hub содержит:

  • бизнес-ключ (customer_bk, order_id, contract_number);

  • техническую информацию:

    • record_source — из какой системы пришла первая запись;
    • load_dttm — когда попала в DV;
    • иногда — хэш бизнес-ключа (hk_customer).

Главные правила:

  • один бизнес-ключ — один хаб (одна строка на сущность, без истории);
  • хаб не знает про атрибуты (имя, email) — только идентичность.

Простейший DDL-скелет:

CREATE TABLE hub_customer (
    hk_customer     BYTEA        PRIMARY KEY,        -- хэш от BK
    customer_bk     VARCHAR(50)  NOT NULL,           -- business key
    record_source   VARCHAR(50)  NOT NULL,
    load_dttm       TIMESTAMP    NOT NULL
);

Link описывает факт связи, например:

  • заказ ↔ клиент,
  • договор ↔ счёт,
  • карта ↔ клиент.

Примеры бизнес-смыслов:

  • link_order_customer — «этот заказ принадлежит этому клиенту»;
  • link_contract_account — «этот договор привязан к этому счёту».

DDL-приблизительно:

CREATE TABLE link_order_customer (
    hk_order_customer  BYTEA PRIMARY KEY,
    hk_order           BYTEA NOT NULL,
    hk_customer        BYTEA NOT NULL,
    record_source      VARCHAR(50) NOT NULL,
    load_dttm          TIMESTAMP   NOT NULL
);

3.3. Satellite — атрибуты и история

Satellite хранит:

  • атрибуты хаба или линка;
  • историю изменений этих атрибутов.

Примеры:

  • sat_customer_info — имя, email, город клиента;
  • sat_customer_segment — сегмент, категория, риск-профиль;
  • sat_order_status — статус заказа.

Типичные поля:

  • ссылка на HUB/LINK (hk_customer, hk_order_customer);
  • атрибуты (email, city, status…);
  • valid_from / valid_to — период действия версии;
  • hashdiff — хэш от всех атрибутов (чтобы понимать, что строка действительно изменилась, а не повторилась);
  • record_source, load_dttm.
CREATE TABLE sat_customer_info (
    hk_customer    BYTEA      NOT NULL,
    hashdiff       BYTEA      NOT NULL,
    valid_from     TIMESTAMP  NOT NULL,
    valid_to       TIMESTAMP  NOT NULL,
    name           VARCHAR(100),
    email          VARCHAR(100),
    city           VARCHAR(50),
    record_source  VARCHAR(50) NOT NULL,
    load_dttm      TIMESTAMP   NOT NULL
);

4. Типы сателлитов в DV 2.0

В DV 2.0 появилось разделение по «ролям» сателлитов. Главное, что стоит знать:

  • Descriptive Satellites — обычные атрибуты (имя, адрес, тариф) с историей.
  • Effectivity Satellites — фокус на периодах действия (valid_from/valid_to), очень похоже на SCD2.
  • Multi-Active Satellites — когда у сущности несколько одновременных значений (например, три активных телефона клиента).
  • Transactional Satellites — события, привязанные к одному хабу/линку (например, журнал изменений статуса).

На практике это разные DDL-«шаблоны» поверх одной и той же идеи: атрибуты + время → отдельная табличка.


5. Raw Vault vs Business Vault: «склад деталей» и «сборочный цех»

Обычно под «Data Vault» люди смешивают два слоя:

flowchart LR
    SRC[Источники] --> STG[STG / ODS]
    STG --> RAW[Raw Vault<br/>Hubs, Links, Sats]
    RAW --> BV[Business Vault<br/>PIT, Bridge, Derived]
    BV --> DM[Data Marts<br/>Star Schema]
    DM --> BI[BI / reports / ML]
  • Raw Vault — это про приём и хранение данных «как есть», но в форме Hub/Link/Sat.
  • Business Vault — это про приведение их в «деловой» вид: с бизнес-правилами, PIT/Bridge и подготовленными представлениями.

5.1. Raw Vault — «всё прилетевшее, аккуратно разложенное по ящичкам»

Raw DV — первый слой поверх STG/ODS:

  • выравниваем ключи;
  • разбираем сущности по Hub/Link/Sat;
  • сохраняем всю историю изменений, не решая ещё «что такое активный клиент» или «успешный заказ».

Характерные черты Raw Vault:

  • минимум бизнес-логики: никаких «клиент активен, если было ≥1 покупки за 90 дней»;
  • все источники показываются «как есть», только приведены к общим ключам;
  • структура стабильна: добавился новый источник → появился новый Sat к тому же Hub.

Это слой для инженеров. Писать по нему отчёты — больно:

  • чтобы узнать «какой у клиента email на дату заказа», нужно JOIN’ить Hub + Sat + Link, плюс фильтровать по valid_from/valid_to или load_dttm;
  • чтобы собрать «портрет клиента» — нужно руками клеить несколько сателлитов.

Поэтому Raw DV почти никогда не является точкой входа для аналитиков.


5.2. Business Vault — «там, где из Lego собирают модули»

Business Vault (BV) — это слой поверх Raw DV, где:

  • появляются бизнес-правила,
  • строятся удобные для чтения «сборные» объекты (таблицы и представления),
  • упрощается доступ к истории.

Кто потребитель BV:

  • разработчики витрин (DM / Star Schema);
  • часть сложных отчётов (особенно с тяжёлой историей);
  • иногда — data scientists, если им нужен богатый, но ещё не «сплющенный» слой.

В BV живут несколько типичных конструкций.

5.2.1. PIT-таблицы (Point-in-Time)

PIT (Point-in-Time) — таблицы, которые отвечают на вопрос:

«Как выглядел объект на дату X

Вместо того чтобы каждый раз писать сложный запрос с диапазонами (valid_from/valid_to), мы заранее готовим таблицу:

CREATE TABLE pit_customer_daily (
    hk_customer   BYTEA,
    as_of_date    DATE,
    hk_sat_info   BYTEA,   -- ссылка на нужную версию sat_customer_info
    hk_sat_segment BYTEA,  -- ссылка на нужную версию sat_customer_segment
    ...
    load_dttm     TIMESTAMP
);

Теперь, чтобы собрать витрину продаж:

  • JOIN fact_orders к pit_customer_daily по order_date = as_of_date;
  • а уже потом — к сателлитам по их ключам.

Выигрыш:

  • сложная логика выбора «правильной версии на дату» живёт в одном месте (ETL PIT);
  • BI и витрины видят «почти плоский» слой.

5.2.2. Bridge-таблицы

Bridge — это «готовые маршруты» по графу Hub/Link.

Например:

  • в DV клиент ↔ договор ↔ счёт ↔ продукт живут в разных хабах/линках;
  • чтобы каждый раз не JOIN’ить весь путь, мы строим bridge_customer_account:
CREATE TABLE bridge_customer_account AS
SELECT DISTINCT
    hk_customer,
    hk_account,
    first_seen_dttm,
    last_seen_dttm
FROM ...

Bridge:

  • упрощают запросы для витрин;
  • фиксируют сложные правила связи (например, что считать «текущим» счётом клиента).

5.2.3. Business-правила и derived-таблицы

В BV логично размещать бизнес-логику, которая:

  • повторяется во многих отчётах;
  • стабильна относительно конкретной витрины.

Примеры:

  • «Активный клиент» — флаг, который вычисляется на основе Raw DV (истории покупок, логинов и т.п.);
  • «Основной тариф» — выбран по набору правил из нескольких источников;
  • «Чистый статус заказа» — свёрнут из цепочки статусов (created → paid → shipped → delivered / cancelled).

Это могут быть как отдельные Sats/Links, так и «логические» таблицы BV:

CREATE TABLE bv_customer_flags AS
SELECT
    hk_customer,
    is_active_30d,
    is_active_90d,
    is_vip,
    load_dttm
FROM ...

Потом витрины берут уже готовые флаги, не дублируя правила.


5.3. Граница между Business Vault и витринами (DM)

Важно не скатиться в две крайности:

  • всё тащить в Raw DV → BV пустой, витрины перегружены логикой;
  • всё тащить в BV → витрины превращаются в тонкий слой SELECT’ов, но BV — новый «монолит».

Полезное правило:

В Business Vault живёт то, что относится к бизнес-сущностям и повторяется. В витринах живёт то, что уникально для конкретного отчёта/дашборда.

Например:

  • «клиент активен, если ≥1 покупки за 90 дней» — это логика уровня клиента → место ей в BV (bv_customer_flags);
  • «клиент попал в эту конкретную маркетинговую воронку» — это логика конкретного отчёта → можно оставить в DM.

5.4. Как выглядит связка Raw DV → BV → DM на примере

Возьмём пример интернет-магазина:

  1. Raw Vault:

    • hub_customer, hub_order;
    • link_order_customer;
    • sat_customer_info, sat_order_status, sat_customer_segment.
  2. Business Vault:

    • pit_customer_daily — «срез клиента по дням»;
    • bv_customer_flags — активность, VIP-статусы, сегменты;
    • bridge_customer_order — связи клиент ↔ заказ с удобными ключами.
  3. DM / Star Schema:

    • dm.fact_sales — факт продаж;
    • dm.dim_customer — уже «плоское» измерение с нужными полями (email_current, segment, is_active_90d…);
    • dm.dim_date, dm.dim_product и т.п.

В результате:

  • Raw DV — технически правильный, историчный и некрасивый;
  • BV — «рабочий слой для инженеров и продвинутых аналитиков»;
  • DM — привычная Звезда для всех остальных.

Кратко:

  • Raw DV без BV — это как держать только структуры hub_*/link_*/sat_* и заставлять всех писать поверх них запросы. Это больно.
  • Business Vault — обязательный промежуточный слой, если вы действительно живёте в Data Vault, а не просто «сложили историю по паттерну Hub/Link/Sat».

6. Пример: клиент и заказы в DV-стиле

Возьмём мини-пример (тот же интернет-магазин):

  • клиент с customer_id = 101 и меняющимся email;
  • заказы order_id = 5001, 5002;
  • статусы заказов.

В Data Vault:

  • hub_customer — одна строка на BK 101;
  • sat_customer_info — несколько строк по мере смены email/города;
  • hub_order — по строке на каждый заказ;
  • link_order_customer — связь заказ ↔ клиент;
  • sat_order_status — история статусов заказа.

Дальше:

  • из Raw DV мы строим PIT:

    • «каким был клиент на дату заказа?»;
  • и уже из PIT + ссылок собираем витрину mart_sales в формате Звезды.

Тут важно, что Raw DV почти не трогается при изменении бизнес-логики — правим BV и витрины.


7. Как это живёт в пайплайне загрузки

Типичный ETL/ELT с DV:

  1. STG/ODS: вытащили данные из источников, почистили типы, привели формат.

  2. Raw Vault:

    • по BK вычислили хэш-ключи для Hubs;
    • создали/обновили Hubs;
    • создали/обновили Links;
    • сравнили hashdiff в Sats → добавили новые версии атрибутов.
  3. Business Vault:

    • собрали PIT-таблицы (одна строка на объект на дату X);
    • добавили бизнес-флаги и derived-поля.
  4. DM (Star):

    • построили факт + измерения, которые уже подходят BI/аналитикам.

За счёт хэш-ключей и hashdiff DV хорошо масштабируется и параллелится: разные сущности и домены могут грузиться независимыми пайплайнами.


8. Плюсы и минусы Data Vault — без романтики

8.1. Плюсы

  • 📜 История «из коробки» Каждое изменение — отдельная запись в сателлите. Ничего не перезатирается.

  • 🧩 Лёгкое добавление источников Новый источник с теми же сущностями = новые сателлиты к тем же хабам.

  • 🔎 Трассировка и аудит Видно, из какого источника, когда и с какими атрибутами прилетела каждая строка.

  • 👥 Параллельная работа команд Разные домены в DV практически не блокируют друг друга.

8.2. Минусы

  • 🧠 Высокий порог входа Нужно понимать SCD, хэш-ключи, нагрузку на JOIN, паттерны загрузки.

  • 📈 Больше таблиц и JOIN’ов Даже простой запрос превращается в «HUB + LINK + 23 SAT + PIT».

  • 🛠 Нужна дисциплина Забыли заполнить record_source/load_dttm — потеряли часть аудита.

  • Плохо подходит для MVP Для 2–3 источников DV обычно дороже, чем классическая 3NF/Звезда.


9. Когда DV стоит использовать, а когда — нет

Подходит, если:

  • у вас зоопарк источников (5+ систем, которые ещё и меняются);
  • важна полная история и аудит (финтех, гос, телеком, крупный банк);
  • команда нацелена на долгую жизнь DWH, а не одноразовый отчёт;
  • есть люди, готовые жить в этой модели (архитектор, data engineer’ы).

Лучше не начинать с DV, если:

  • это первый DWH в компании;
  • 1–3 источника и нет жёстких требований по аудиту;
  • команда малая (1–2 инженера + аналитик) и сроки жмут;
  • задача звучит как «дайте отчёты к кварталу», а не «построим платформу на 5 лет».

В таких случаях честнее (и дешевле) начать с:

stg → ods → dds (3NF/простая Звезда с SCD2) → dm (Звезда)

А DV оставить как следующий шаг, когда появятся реальные боли, которые он решает.


10. Как учить Data Vault дальше

Если после этой статьи хочется «копнуть глубже», можно идти по такой траектории:

  1. Повторить базу:

    • слои STG/ODS/DDS/DM;
    • факты/измерения;
    • SCD Type 2.
  2. Почитать/посмотреть про DV 2.0:

    • книги/доки Линстедта (Dan Linstedt, Data Vault 2.0),
    • практические доклады (особенно от телекомов и банков — там DV живой).
  3. Сделать игрушечный DV-проект:

    • взять тот же интернет-магазин,
    • смоделировать hub_customer, hub_order, link_order_customer, 23 сателлита,
    • написать пару запросов: «какой был клиент на дату заказа?», «как менялся статус заказа?».
  4. Посмотреть на гибриды:

    • DV в ядре (Raw+Business Vault),
    • Звезда на витринах.