- Зачем: - относительные ссылки на каталоги sql/ и data/ давали 404 на GitHub Pages; - em-dash в заголовках нарушает правило AGENTS.md (расходятся слаги GitHub/MkDocs); - 2-пробельная вложенность и списки без пустой строки ломали рендер в MkDocs - Что: - ссылки на папки заменены на GitHub-tree-ссылки (работают на обеих платформах); - в 26 заголовках « — » заменено на «: » или убрано (README, SCD, DataVault, домашка); - пустая строка перед списком «Главные правила» в DataVault; - вложенные списки DataVault переведены на 4-пробельный отступ - Проверка: - mkdocs build --strict (без ошибок); - grep по репо — якорных ссылок на старые слаги заголовков нет; - grep по site/dwh-modeling/*.html — списки рендерятся <ul>/<li>, вложенность сохранена Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
652 lines
34 KiB
Markdown
652 lines
34 KiB
Markdown
# **Data Vault 2.0: как собрать хранилище из конструктора**
|
||
|
||
## 📚 Оглавление
|
||
|
||
1. [Зачем вообще нужен Data Vault?](#1-зачем-вообще-нужен-data-vault)
|
||
2. [Интуиция: DV как конструктор Lego](#2-интуиция-dv-как-конструктор-lego)
|
||
3. [Три типа таблиц в Data Vault 2.0](#3-три-типа-таблиц-в-data-vault-20)
|
||
4. [Типы сателлитов в DV 2.0](#4-типы-сателлитов-в-dv-20)
|
||
5. [Raw Vault и Business Vault](#5-raw-vault-и-business-vault)
|
||
6. [Пример: клиент и заказы в DV-стиле](#6-пример-клиент-и-заказы-в-dv-стиле)
|
||
7. [Как это живёт в пайплайне загрузки](#7-как-это-живёт-в-пайплайне-загрузки)
|
||
8. [Плюсы и минусы Data Vault](#8-плюсы-и-минусы-data-vault)
|
||
9. [Когда DV стоит использовать, а когда нет](#9-когда-dv-стоит-использовать-а-когда-нет)
|
||
10. [Шпаргалка для собеседования](#10-шпаргалка-для-собеседования)
|
||
|
||
---
|
||
|
||
## 1. Зачем вообще нужен Data Vault?
|
||
|
||
Большинство знакомятся с хранилищами через две модели:
|
||
|
||
* **3NF** (Инмон) — нормализованное ядро: много таблиц, строгие связи, минимум дублирования.
|
||
* **Звезда (Star Schema)** (Кимбалл) — витрины под отчёты: факт + несколько «плоских» измерений.
|
||
|
||
Этого хватает для:
|
||
|
||
* 2–5 источников,
|
||
* относительно стабильных схем,
|
||
* задач типа «сделать отчёт для маркетинга/финансов».
|
||
|
||
Проблемы начинаются, когда:
|
||
|
||
* источников **становится десяток и больше** (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
|
||
varchar record_source
|
||
timestamp load_dttm
|
||
}
|
||
|
||
HUB_ORDER {
|
||
varchar order_id PK
|
||
varchar record_source
|
||
timestamp load_dttm
|
||
}
|
||
|
||
LINK_ORDER_CUSTOMER {
|
||
varchar link_key PK
|
||
varchar order_id FK
|
||
varchar customer_bk FK
|
||
varchar record_source
|
||
timestamp load_dttm
|
||
}
|
||
|
||
SAT_CUSTOMER_INFO {
|
||
varchar customer_bk FK
|
||
varchar hashdiff
|
||
varchar name
|
||
varchar email
|
||
varchar city
|
||
varchar record_source
|
||
timestamp load_dttm
|
||
}
|
||
|
||
SAT_ORDER_STATUS {
|
||
varchar link_key FK
|
||
varchar hashdiff
|
||
varchar status
|
||
varchar record_source
|
||
timestamp load_dttm
|
||
}
|
||
```
|
||
|
||
Как это читать:
|
||
|
||
* 🔴 HUB_CUSTOMER / HUB_ORDER — «этот клиент существует», «этот заказ существует»;
|
||
* ⚪ LINK_ORDER_CUSTOMER — «именно этот заказ сделал именно этот клиент»;
|
||
* 🟡 SAT_… — как менялись атрибуты (email, статус и т.п.) во времени.
|
||
|
||
На схеме выше для наглядности показаны бизнес-ключи (`customer_bk`, `order_id`, `link_key`), а в DDL-примерах дальше используются уже хэш-ключи (`hk_*`) — это два уровня детализации одной и той же модели.
|
||
|
||
## 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
|
||
);
|
||
```
|
||
|
||
Конкретные типы данных (`BYTEA`, длины `VARCHAR`, детали `hashdiff`) и реализации хэш‑ключей можно не запоминать: на старте важнее понять саму идею — у сущностей есть стабильные ключи, а все изменения атрибутов мы записываем отдельными версиями в сателлитах.
|
||
|
||
### 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 и т.п.);
|
||
* hashdiff — хэш от всех атрибутов, чтобы понять, изменилась ли строка;
|
||
* record_source, load_dttm — источник и момент загрузки версии.
|
||
|
||
```sql
|
||
CREATE TABLE sat_customer_info (
|
||
hk_customer BYTEA NOT NULL,
|
||
hashdiff BYTEA NOT NULL,
|
||
name VARCHAR(100),
|
||
email VARCHAR(100),
|
||
city VARCHAR(50),
|
||
record_source VARCHAR(50) NOT NULL,
|
||
load_dttm TIMESTAMP NOT NULL
|
||
);
|
||
```
|
||
|
||
Главная мысль: DV заставляет явно разделять идентичность, связи и атрибуты с историей. Это делает модель сложнее на вид, но гораздо устойчивее к изменениям источников.
|
||
|
||
Когда нужны именно бизнес-периоды действия («с/по»), их удобнее моделировать не в базовом сателлите, а отдельными effectivity-сателлитами или через PIT-таблицы в Business Vault.
|
||
|
||
## 4. Типы сателлитов в DV 2.0
|
||
|
||
> Если вы только знакомитесь с DV, этот раздел можно прочитать по диагонали: для собеседования важно скорее знать, что такие роли бывают, чем разбираться во всех нюансах.
|
||
|
||
В DV 2.0 появилось разделение по «ролям» сателлитов. Главное, что стоит знать:
|
||
|
||
* **Descriptive Satellites** — обычные атрибуты (имя, адрес, тариф) с историей.
|
||
* **Effectivity Satellites** — фокус на периодах действия (`valid_from` / `valid_to`), очень похоже на SCD2.
|
||
* **Multi-Active Satellites** — когда у сущности несколько одновременных значений (например, три активных телефона клиента).
|
||
* **Transactional Satellites** — события, привязанные к одному хабу/линку (например, журнал изменений статуса).
|
||
|
||
На практике это разные DDL-«шаблоны» поверх одной и той же идеи:
|
||
**атрибуты + время → отдельная табличка.**
|
||
|
||
---
|
||
|
||
## 5. Raw Vault и Business Vault
|
||
|
||
Обычно под «Data Vault» люди смешивают два слоя:
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
SRC[Источники] --> STG[STG / ODS]
|
||
STG --> RAW[Raw Vault
|
||
Hubs, Links, Sats]
|
||
RAW --> BV[Business Vault
|
||
PIT, Bridge, Derived]
|
||
BV --> DM[Data Marts
|
||
Star Schema]
|
||
DM --> BI[BI / ML]
|
||
```
|
||
|
||
* **Raw Vault** — это про приём и хранение данных «как есть», но уже в форме Hub / Link / Satellite.
|
||
* **Business Vault** — это про приведение этих данных в более «деловой» вид: с бизнес-правилами, PIT/Bridge и подготовленными представлениями.
|
||
|
||
### 5.1. Raw Vault: «всё прилетевшее, аккуратно разложенное по ящичкам»
|
||
|
||
Raw DV — первый слой поверх STG / ODS:
|
||
|
||
* выравниваем ключи;
|
||
* разбираем сущности по Hub / Link / Sat;
|
||
* сохраняем всю историю изменений, не решая ещё, что такое «активный клиент» или «успешный заказ».
|
||
|
||
Характерные черты Raw Vault:
|
||
|
||
* минимум бизнес-логики:
|
||
|
||
* никаких правил вроде «клиент активен, если была хотя бы одна покупка за 90 дней»;
|
||
* все источники показываются «как есть», только приведены к общим ключам;
|
||
* структура стабильна: добавился новый источник → появился новый Satellite к тому же Hub.
|
||
|
||
### 5.2. Business Vault: «там, где из Lego собирают модули»
|
||
|
||
Business Vault (BV) — следующий слой над Raw DV:
|
||
|
||
* здесь применяются бизнес-правила (что считать активным клиентом, как трактовать статусы);
|
||
* здесь строятся вспомогательные структуры:
|
||
|
||
* PIT-таблицы,
|
||
* Bridge-таблицы,
|
||
* агрегаты и derived-таблицы.
|
||
|
||
Именно из BV чаще всего строятся витрины в формате Звезды, к которым подключаются BI и отчётность.
|
||
|
||
Если сильно упростить:
|
||
|
||
* Raw DV → «мы всё собрали»;
|
||
* Business Vault → «мы это привели в вид, с которым удобно жить»;
|
||
* DM → «мы вынесли это на витрину в понятной форме».
|
||
|
||
#### 5.2.1. PIT-таблицы (Point-in-Time)
|
||
|
||
> Раздел для любопытных: PIT-таблицы — уже продвинутая тема, на первом знакомстве с DV её можно смело пропустить и вернуться позже.
|
||
|
||
PIT (Point-in-Time) решает очень конкретную боль:
|
||
|
||
> «Покажи, как объект выглядел **на дату X**, но так, чтобы запрос был простым».
|
||
|
||
Если у нас есть несколько сателлитов с историей (например, `sat_customer_info`, `sat_customer_segment`, `sat_customer_risk`), то без PIT любой запрос превращается в пачку условий
|
||
`as_of_date >= valid_from AND (valid_to IS NULL OR as_of_date < valid_to)` (в effectivity-сателлитах или любых таблицах с периодами действия) или оконных функций.
|
||
|
||
**Идея PIT:**
|
||
|
||
Мы заранее считаем «словарь» вида:
|
||
|
||
> для каждой пары (объект, дата) — какие версии сателлитов на эту дату актуальны.
|
||
|
||
Упрощённый пример структуры:
|
||
|
||
```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
|
||
);
|
||
```
|
||
|
||
Внутри логически это выглядит так:
|
||
|
||
| hk_customer | as_of_date | hk_sat_info | hk_sat_segment |
|
||
| ----------- | ---------- | ------------ | -------------- |
|
||
| 101 | 2023-06-10 | hash_info_v1 | hash_seg_v1 |
|
||
| 101 | 2023-06-11 | hash_info_v2 | hash_seg_v1 |
|
||
| 101 | 2023-06-12 | hash_info_v2 | hash_seg_v2 |
|
||
|
||
Дальше витрина продаж вместо сложных `BETWEEN` делает простой JOIN:
|
||
|
||
```sql
|
||
SELECT
|
||
s.sale_date,
|
||
s.amount,
|
||
seg.segment,
|
||
info.city
|
||
FROM fact_sales s
|
||
JOIN pit_customer_daily pit
|
||
ON pit.hk_customer = s.hk_customer
|
||
AND pit.as_of_date = s.sale_date
|
||
JOIN sat_customer_segment seg
|
||
ON seg.hk_customer = pit.hk_customer
|
||
AND seg.hashdiff = pit.hk_sat_segment
|
||
JOIN sat_customer_info info
|
||
ON info.hk_customer = pit.hk_customer
|
||
AND info.hashdiff = pit.hk_sat_info;
|
||
```
|
||
|
||
Мы **один раз** дорого посчитали PIT (по расписанию),
|
||
и дальше все витрины просто используют эту «справочную таблицу».
|
||
|
||
Коротко: **PIT — это таблица, где заранее записано, какая версия данных была актуальна на дату X.**
|
||
|
||
#### 5.2.2. Bridge-таблицы
|
||
|
||
> Тоже продвинутый приём: Bridge-таблицы чаще нужны в боевых хранилищах, чем на первых собеседованиях.
|
||
|
||
Bridge-таблицы отвечают на другой вопрос:
|
||
|
||
> «Какие объекты **в итоге** связаны между собой через длинную цепочку Links?»
|
||
|
||
В Data Vault связь между двумя сущностями редко бывает «одним JOIN’ом». Чаще это цепочка:
|
||
|
||
* клиент → договор → счёт → карта → транзакция;
|
||
* подразделение → филиал → торговая точка → чек;
|
||
* компания → дочерняя компания → проект → контракт → платёж.
|
||
|
||
Если каждый раз в витринах писать все эти JOIN’ы, запросы становятся:
|
||
|
||
* длинными и хрупкими;
|
||
* плохо читаемыми;
|
||
* дублируются во множестве отчётов.
|
||
|
||
**Идея Bridge:**
|
||
|
||
Сделать отдельную таблицу, где заранее развернуть «финальные» пары:
|
||
|
||
```sql
|
||
CREATE TABLE bridge_customer_trx (
|
||
hk_customer BYTEA,
|
||
hk_trx BYTEA
|
||
-- опционально: даты действия связи, тип связи и т.п.
|
||
);
|
||
```
|
||
|
||
Такую таблицу мы считаем **в ETL**, один раз по расписанию, а не в каждом запросе.
|
||
Условный псевдокод построения:
|
||
|
||
```sql
|
||
INSERT INTO bridge_customer_trx (hk_customer, hk_trx)
|
||
SELECT DISTINCT
|
||
c.hk_customer,
|
||
t.hk_trx
|
||
FROM hub_customer c
|
||
JOIN link_customer_contract lcc ON lcc.hk_customer = c.hk_customer
|
||
JOIN hub_contract ct ON ct.hk_contract = lcc.hk_contract
|
||
JOIN link_contract_account lca ON lca.hk_contract = ct.hk_contract
|
||
JOIN hub_account acc ON acc.hk_account = lca.hk_account
|
||
JOIN link_account_card lac ON lac.hk_account = acc.hk_account
|
||
JOIN hub_card card ON card.hk_card = lac.hk_card
|
||
JOIN link_card_trx lct ON lct.hk_card = card.hk_card
|
||
JOIN hub_trx t ON t.hk_trx = lct.hk_trx;
|
||
```
|
||
|
||
После этого витрина может использовать уже готовый Bridge:
|
||
|
||
```sql
|
||
SELECT
|
||
c.customer_bk,
|
||
SUM(t.amount) AS total_amount
|
||
FROM hub_customer c
|
||
JOIN bridge_customer_trx bct
|
||
ON bct.hk_customer = c.hk_customer
|
||
JOIN hub_trx t
|
||
ON t.hk_trx = bct.hk_trx
|
||
WHERE t.trx_date >= CURRENT_DATE - INTERVAL '30 day'
|
||
GROUP BY c.customer_bk;
|
||
```
|
||
|
||
Сложный путь по Links мы спрятали внутрь bridge‑таблицы,
|
||
а витрины работают с простой связкой «клиент ↔ транзакция».
|
||
|
||
Коротко:
|
||
|
||
* **PIT** — про «какая версия атрибутов была на дату X»;
|
||
* **Bridge** — про «какие объекты в итоге связаны друг с другом по длинному маршруту».
|
||
|
||
Bridge — это **не обязательный элемент DV**, а инструмент оптимизации.
|
||
Он нужен тогда, когда цепочки Links становятся длинными и повторяются во многих отчётах.
|
||
|
||
#### 5.2.3. Business-правила и derived-таблицы
|
||
|
||
> Этот раздел полезен, чтобы увидеть, как DV помогает «прятать» повторяющуюся бизнес-логику, но для базового понимания Data Vault его можно оставить «на потом».
|
||
|
||
В BV логично размещать бизнес-логику, которая:
|
||
|
||
* повторяется во многих отчётах;
|
||
* достаточно стабильна.
|
||
|
||
Примеры:
|
||
|
||
* флаг «активный клиент», который вычисляется на основе истории покупок и логинов;
|
||
* «основной тариф», выбранный по набору правил из нескольких источников;
|
||
* «чистый статус заказа», свёрнутый из цепочки статусов (created → paid → shipped → delivered / cancelled).
|
||
|
||
Это могут быть как отдельные Satellites / Links, так и логические таблицы BV с уже посчитанными флагами и агрегатами.
|
||
|
||
### 5.3. Граница между Business Vault и витринами (DM)
|
||
|
||
Важно проговорить границу:
|
||
|
||
* Business Vault — ещё про данные и историю;
|
||
* DM (Data Marts) — уже про конкретные бизнес-сценарии и удобство BI.
|
||
|
||
Витрины можно переделывать, не трогая BV, пока вы не меняете фундаментальные бизнес-правила.
|
||
|
||
### 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 — привычная Звезда для всех остальных.
|
||
|
||
## 6. Пример: клиент и заказы в DV-стиле
|
||
|
||
Возьмём мини‑пример всё того же интернет‑магазина.
|
||
|
||
У нас есть:
|
||
|
||
* клиент с `customer_id = 101`, у которого иногда меняется email и город;
|
||
* два заказа: `order_id = 5001` и `order_id = 5002`;
|
||
* статусы заказов: `created → paid → shipped → delivered`.
|
||
|
||
### Что появляется в Raw Vault
|
||
|
||
В Data Vault это раскладывается на несколько таблиц:
|
||
|
||
* `hub_customer` — по одной строке на каждого клиента (BK = `customer_id`).
|
||
* `hub_order` — по одной строке на каждый заказ (BK = `order_id`).
|
||
* `link_order_customer` — связь «какой заказ сделал какой клиент».
|
||
* `sat_customer_info` — история атрибутов клиента (имя, email, город).
|
||
* `sat_order_status` — история статусов заказа.
|
||
|
||
Примерно так это выглядит логически:
|
||
|
||
```text
|
||
HUB_CUSTOMER
|
||
--------------------------------------
|
||
customer_bk | record_source | load_dttm
|
||
--------------------------------------
|
||
101 | CRM | 2023-01-10 10:00
|
||
|
||
SAT_CUSTOMER_INFO
|
||
customer_bk | load_dttm | hashdiff | email | city
|
||
---------------------------------------------------------------------------
|
||
101 | 2023-01-10 10:00 | ... | a@example.com | Moscow
|
||
101 | 2023-06-01 09:00 | ... | alice@newmail.com | Moscow
|
||
|
||
HUB_ORDER
|
||
--------------------------------------
|
||
order_id | record_source | load_dttm
|
||
--------------------------------------
|
||
5001 | SHOP | 2023-06-10 12:00
|
||
5002 | SHOP | 2023-06-11 09:30
|
||
|
||
SAT_ORDER_STATUS
|
||
order_id | load_dttm | hashdiff | status
|
||
--------------------------------------------------------------
|
||
5001 | 2023-06-10 12:00 | ... | created
|
||
5001 | 2023-06-10 12:05 | ... | paid
|
||
5001 | 2023-06-11 09:00 | ... | shipped
|
||
... | ... | ...
|
||
```
|
||
|
||
Ключевая идея: **любое изменение** (email, статус) — это **новая строка** в соответствующем Satellite.
|
||
|
||
### Как из этого получить витрину продаж
|
||
|
||
Дальше нам нужно привычное измерение `dim_customer` и факт `fact_sales` в формате Звезды.
|
||
|
||
Обычно цепочка выглядит так:
|
||
|
||
1. В Business Vault строим `PIT`‑таблицу по клиентам:
|
||
|
||
* для каждой даты (или дня, или часа) знаем, какая строка из `sat_customer_info` была актуальна.
|
||
2. Строим витрину `dm.dim_customer`:
|
||
|
||
* на выбранную дату берём нужную версию из `sat_customer_info`;
|
||
* добавляем флаги/сегменты из других Satellites/BV‑таблиц.
|
||
3. Строим факт `dm.fact_sales`, где каждая строка — заказ или позиция заказа.
|
||
|
||
Итог: на витрине мы видим «плоского» клиента (одна строка → текущее имя/город/email на момент заказа),
|
||
хотя внутри DV лежит полная, аккуратно разложенная история.
|
||
|
||
## 7. Как это живёт в пайплайне загрузки
|
||
|
||
Посмотрим теперь, как DV вписывается в обычный ETL/ELT‑пайплайн.
|
||
|
||
Типичный поток выглядит так:
|
||
|
||
1. **STG / ODS — приём и первичная обработка**
|
||
|
||
* Подключаемся к источникам (CRM, биллинг, сайт, партнёрские выгрузки).
|
||
* Забираем инкременты (CDC, выгрузки по расписанию, API).
|
||
* Приводим типы данных, чистим очевидный мусор, нормализуем форматы дат и т.п.
|
||
|
||
2. **Raw Vault — приземление в Hub / Link / Satellite**
|
||
|
||
* Из STG/ODS считаем хэш‑ключи для бизнес‑ключей (HK для Hubs).
|
||
* Создаём/обновляем **Hubs** — если бизнес‑ключ новый, заводим запись.
|
||
* Создаём/обновляем **Links** — фиксируем связи между сущностями.
|
||
* Для **Satellites** считаем `hashdiff` по атрибутам и добавляем новые строки
|
||
только если что‑то реально изменилось.
|
||
|
||
3. **Business Vault — бизнес‑правила и ускорители**
|
||
|
||
* Строим PIT‑таблицы, чтобы быстро получать срез «на дату X».
|
||
* Строим Bridge‑таблицы для сложных цепочек связей.
|
||
* Вычисляем стабильные бизнес‑флаги и derived‑атрибуты (активность, сегменты, статусы).
|
||
|
||
4. **DM / Star Schema — витрины для отчётов и аналитики**
|
||
|
||
* На основе BV собираем факты и измерения в формате Звезды.
|
||
* Под это уже настраиваются BI‑инструменты, отчётность, дашборды.
|
||
|
||
Чем это отличается от классического «STG → ODS → DDS → DM»:
|
||
|
||
* слой DDS в DV‑подходе часто фактически превращается в **Raw+Business Vault**;
|
||
* вместо одной «большой» нормализованной схемы ядра у нас набор Lego‑модулей (Hubs/Links/Sats), которые за счёт хэш‑ключей и независимой загрузки доменов проще масштабировать и развивать/грузить параллельно.
|
||
|
||
DV не отменяет STG/ODS/DM — он скорее **раскладывает слой DDS на более мелкие и управляемые детали**.
|
||
|
||
## 8. Плюсы и минусы Data Vault
|
||
|
||
### 8.1. Плюсы
|
||
|
||
* 📜 **История «из коробки»**
|
||
Каждое изменение — отдельная запись в сателлите. Ничего не перезатирается.
|
||
|
||
* 🧩 **Лёгкое добавление источников**
|
||
Новый источник с теми же сущностями = новые сателлиты к тем же хабам.
|
||
|
||
* 🔎 **Трассировка и аудит**
|
||
Видно, из какого источника, когда и с какими атрибутами прилетела каждая строка.
|
||
|
||
* 👥 **Параллельная работа команд**
|
||
Разные домены в DV практически не блокируют друг друга.
|
||
|
||
### 8.2. Минусы
|
||
|
||
* 🧠 **Высокий порог входа**
|
||
Нужно понимать SCD, хэш‑ключи, нагрузку на JOIN, паттерны загрузки.
|
||
|
||
* 📈 **Больше таблиц и JOIN’ов**
|
||
Даже простой запрос превращается в «HUB + LINK + 2–3 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:
|
||
|
||
- Data Vault — это модель хранилища, которая разделяет идентичность (Hubs), связи (Links) и атрибуты с историей (Satellites), чтобы проще переживать изменения источников.
|
||
- В DV есть три типа таблиц: `Hub` (бизнес-ключи сущностей), `Link` (связи между сущностями) и `Satellite` (атрибуты и их история).
|
||
- Raw Vault — слой, где данные складываются «как есть» в виде Hub/Link/Sat, Business Vault — слой с бизнес-правилами, PIT/Bridge и подготовленными представлениями для витрин.
|
||
- История в DV хранится «из коробки»: каждое изменение атрибутов — новая строка в сателлите, прошлые значения не затираются.
|
||
- DV хорошо подходит, когда много источников, они часто меняются и важен аудит; для маленького, простого DWH обычно хватает 3NF/Звезды.
|
||
- Для собеседования важно уметь связать всё вместе: объяснить Hub/Link/Satellite, отличия Raw и Business Vault и то, что хэш-ключи и независимые сателлиты позволяют параллельно загружать разные сущности, не ломая историю и аудит.
|