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

553 lines
24 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# **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, статус, тариф) + история изменений.
Главная идея:
> **Идентичность, связи и атрибуты живут отдельно.**
> Тогда изменения в одном не ломают другое.
На картинке это выглядит примерно так:
```mermaid
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-скелет:
```sql
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
);
```
### 3.2. Link — связи между сущностями
**Link** описывает факт связи, например:
* заказ ↔ клиент,
* договор ↔ счёт,
* карта ↔ клиент.
Примеры бизнес-смыслов:
* `link_order_customer` — «этот заказ принадлежит этому клиенту»;
* `link_contract_account` — «этот договор привязан к этому счёту».
DDL-приблизительно:
```sql
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`.
```sql
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» люди смешивают два слоя:
```mermaid
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`), мы заранее готовим таблицу:
```sql
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`:
```sql
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:
```sql
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),
* Звезда на витринах.