Data Vault 2.0
Data Vault 2.0 — гибкое хранилище¶
Data Vault — методология проектирования хранилища данных, предложенная Дэном Линстедтом (Dan Linstedt) в начале 2000-х. Если Star Schema оптимизирована для аналитики, то Data Vault оптимизирована для загрузки и хранения данных из множества источников.
Это не замена Кимбаллу, а другой слой: Data Vault живёт между источниками и витринами. Данные сначала попадают в Data Vault (raw vault), а затем преобразуются в звёздную схему для BI.
Зачем нужен Data Vault¶
Представь, что у тебя 10 систем-источников: CRM, ERP, сайт, мобильное приложение. Каждая система по-своему описывает клиента, товар, заказ. Проблемы:
- Конфликты ключей — один и тот же клиент может иметь ID = 42 в CRM и ID = 7891 в ERP
- Разные схемы — поля не совпадают, форматы дат отличаются
- Частые изменения — источники меняют структуру, и всё хранилище ломается
- Аудит — нужно знать, откуда пришла каждая запись и когда
Кулинарная аналогия
Star Schema — это сервировка стола для гостей. Data Vault — это хорошо организованная кладовая, где каждый ингредиент подписан, датирован и можно отследить, от какого поставщика он пришёл.
Три типа таблиц¶
Data Vault состоит из трёх типов таблиц: Hub, Link и Satellite.
Hub — бизнес-сущность¶
Hub хранит уникальные бизнес-ключи — идентификаторы сущностей из источников.
Правила: - Содержит только бизнес-ключ, хэш-ключ, метаданные загрузки - Никаких описательных атрибутов - Одна строка на один уникальный бизнес-ключ - Не меняется после вставки
CREATE TABLE hub_customer (
-- хэш бизнес-ключа (MD5/SHA-1 от customer_id + source)
hub_customer_hk CHAR(32) PRIMARY KEY,
-- натуральный бизнес-ключ
customer_id VARCHAR(50) NOT NULL,
-- метаданные загрузки
load_date TIMESTAMP NOT NULL DEFAULT NOW(),
record_source VARCHAR(100) NOT NULL
);
CREATE TABLE hub_product (
hub_product_hk CHAR(32) PRIMARY KEY,
product_id VARCHAR(50) NOT NULL,
load_date TIMESTAMP NOT NULL DEFAULT NOW(),
record_source VARCHAR(100) NOT NULL
);
CREATE TABLE hub_order (
hub_order_hk CHAR(32) PRIMARY KEY,
order_id VARCHAR(50) NOT NULL,
load_date TIMESTAMP NOT NULL DEFAULT NOW(),
record_source VARCHAR(100) NOT NULL
);
Зачем хэш-ключ?
Хэш от бизнес-ключа (MD5('CRM|42')) заменяет суррогатный ключ. Его можно вычислить заранее, не обращаясь к базе, что упрощает параллельную загрузку из разных источников.
Link — связь между сущностями¶
Link фиксирует отношения между двумя или более хабами.
Правила: - Содержит хэш-ключи связанных хабов + свой хэш-ключ - Одна строка на одну уникальную комбинацию - Не содержит описательных атрибутов
CREATE TABLE link_order_customer (
link_order_customer_hk CHAR(32) PRIMARY KEY,
hub_order_hk CHAR(32) NOT NULL REFERENCES hub_order(hub_order_hk),
hub_customer_hk CHAR(32) NOT NULL REFERENCES hub_customer(hub_customer_hk),
load_date TIMESTAMP NOT NULL DEFAULT NOW(),
record_source VARCHAR(100) NOT NULL
);
CREATE TABLE link_order_product (
link_order_product_hk CHAR(32) PRIMARY KEY,
hub_order_hk CHAR(32) NOT NULL REFERENCES hub_order(hub_order_hk),
hub_product_hk CHAR(32) NOT NULL REFERENCES hub_product(hub_product_hk),
load_date TIMESTAMP NOT NULL DEFAULT NOW(),
record_source VARCHAR(100) NOT NULL
);
Satellite — описательные атрибуты¶
Satellite хранит контекст (атрибуты) хаба или линка, с полной историей изменений.
Правила:
- Привязан к одному хабу или линку через хэш-ключ
- Содержит описательные атрибуты + load_date + hash_diff
- Каждое изменение атрибутов — новая строка (полная историзация)
- hash_diff — хэш от всех атрибутов для быстрого сравнения
-- Атрибуты клиента
CREATE TABLE sat_customer_details (
hub_customer_hk CHAR(32) NOT NULL REFERENCES hub_customer(hub_customer_hk),
load_date TIMESTAMP NOT NULL,
-- хэш от всех атрибутов — для быстрого сравнения «изменилось ли что-то»
hash_diff CHAR(32) NOT NULL,
name VARCHAR(100),
email VARCHAR(200),
city VARCHAR(100),
segment VARCHAR(50),
record_source VARCHAR(100) NOT NULL,
PRIMARY KEY (hub_customer_hk, load_date)
);
-- Атрибуты товара
CREATE TABLE sat_product_details (
hub_product_hk CHAR(32) NOT NULL REFERENCES hub_product(hub_product_hk),
load_date TIMESTAMP NOT NULL,
hash_diff CHAR(32) NOT NULL,
name VARCHAR(200),
category VARCHAR(100),
unit_price NUMERIC(10,2),
record_source VARCHAR(100) NOT NULL,
PRIMARY KEY (hub_product_hk, load_date)
);
-- Атрибуты связи заказ-товар (количество, цена)
CREATE TABLE sat_order_product (
link_order_product_hk CHAR(32) NOT NULL
REFERENCES link_order_product(link_order_product_hk),
load_date TIMESTAMP NOT NULL,
hash_diff CHAR(32) NOT NULL,
quantity INT,
unit_price NUMERIC(10,2),
total_amount NUMERIC(12,2),
record_source VARCHAR(100) NOT NULL,
PRIMARY KEY (link_order_product_hk, load_date)
);
Загрузка данных¶
Загрузка в Data Vault всегда идёт в определённом порядке: сначала хабы, потом линки, потом сателлиты.
-- 1. Загрузка хаба клиентов
INSERT INTO hub_customer (hub_customer_hk, customer_id, record_source)
SELECT
MD5('CRM|' || customer_id::TEXT),
customer_id::TEXT,
'CRM'
FROM source_customers s
WHERE NOT EXISTS (
SELECT 1 FROM hub_customer h
WHERE h.hub_customer_hk = MD5('CRM|' || s.customer_id::TEXT)
);
-- 2. Загрузка сателлита (только если данные изменились)
INSERT INTO sat_customer_details
(hub_customer_hk, load_date, hash_diff, name, email, city, segment, record_source)
SELECT
MD5('CRM|' || s.customer_id::TEXT),
NOW(),
MD5(s.name || '|' || s.email || '|' || COALESCE(s.city, '')),
s.name,
s.email,
s.city,
s.segment,
'CRM'
FROM source_customers s
WHERE NOT EXISTS (
SELECT 1 FROM sat_customer_details sat
WHERE sat.hub_customer_hk = MD5('CRM|' || s.customer_id::TEXT)
AND sat.hash_diff = MD5(s.name || '|' || s.email || '|' || COALESCE(s.city, ''))
);
Идемпотентность
Загрузка в Data Vault идемпотентна: повторный запуск с теми же данными не создаст дубликатов. Хабы проверяют уникальность по хэш-ключу, сателлиты — по hash_diff.
Получение данных: Point-in-Time и Bridge¶
Запросы напрямую к Data Vault громоздки. Для аналитики строят витрины (Star Schema) поверх Data Vault. Но если нужно получить данные на конкретный момент:
Актуальное состояние клиента¶
-- Последняя версия атрибутов каждого клиента
SELECT
h.customer_id,
s.name,
s.email,
s.city,
s.segment
FROM hub_customer h
JOIN sat_customer_details s ON h.hub_customer_hk = s.hub_customer_hk
WHERE s.load_date = (
SELECT MAX(load_date)
FROM sat_customer_details
WHERE hub_customer_hk = h.hub_customer_hk
);
Состояние на конкретную дату¶
-- Атрибуты клиента на 15 января 2026
SELECT
h.customer_id,
s.name,
s.email,
s.segment
FROM hub_customer h
JOIN sat_customer_details s ON h.hub_customer_hk = s.hub_customer_hk
WHERE s.load_date = (
SELECT MAX(load_date)
FROM sat_customer_details
WHERE hub_customer_hk = h.hub_customer_hk
AND load_date <= '2026-01-15'
);
Data Vault vs Star Schema¶
| Критерий | Star Schema | Data Vault |
|---|---|---|
| Назначение | Аналитика, BI | Интеграция и хранение |
| Оптимизирована для | Чтения (SELECT) | Загрузки (INSERT) |
| Историзация | Через SCD | Встроена (сателлиты) |
| Добавление источника | Может потребовать перестройки | Добавить хаб/линк/сателлит |
| Аудит | Ограничен | Полный (record_source, load_date) |
| Сложность запросов | Простые (fact + dim) | Сложные (hub + link + sat) |
| Когда применять | Витрины для BI | Промежуточный слой хранилища |
Типичная архитектура
На практике Data Vault и Star Schema не конкурируют, а дополняют друг друга:
Источники → Staging → Data Vault (Raw Vault) → Business Vault → Star Schema (витрины) → BI
Что запомнить¶
- Data Vault состоит из трёх типов таблиц: Hub (ключи), Link (связи), Satellite (атрибуты)
- Хэш-ключи вместо суррогатных — упрощают параллельную загрузку
- Полная историзация встроена в сателлиты через
load_date - Аудит «из коробки» — каждая запись знает, откуда пришла и когда
- Data Vault — не замена Star Schema, а промежуточный слой между источниками и витринами
- Подходит, когда много источников, частые изменения схем, нужен полный аудит