571 lines
22 KiB
Markdown
571 lines
22 KiB
Markdown
# Модели данных и слои DWH (для менти)
|
|
|
|
**Цель и аудитория:** эта статья для начинающих аналитиков/инженеров данных, которые уже умеют базовый SQL (SELECT/JOIN/GROUP BY/WHERE) и переходят к моделированию данных и архитектуре хранилища (Data Warehouse, DWH). Здесь даём общее представление: **зачем слои**, **что такое модели**, **чем они отличаются**, где обычно живут **факты/измерения/SCD**. **Выбор модели** в продакшен за вас делать не будем — на этом этапе важно понять, *зачем разные модели существуют* и *какие задачи они решают*.
|
|
|
|
---
|
|
|
|
## ЧАСТЬ A. Исходный черновик статьи (переделан в чистовик с лёгкой правкой опечаток)
|
|
|
|
Ниже — аккуратно отредактированная версия вашего плана с диаграммами Mermaid. Сохранили исходную структуру и формулировки, поправили опечатки и выровняли стиль именования.
|
|
|
|
---
|
|
|
|
# **Структура статьи про хранилище данных**
|
|
|
|
1. Введение. Проблема аналитики в OLTP
|
|
2. Учебный пример. Интернет-магазин
|
|
3. Архитектура. Зачем слои?
|
|
4. Путешествие данных. STG → ODS → DDS → DM
|
|
5. Базовые понятия. Факты, Измерения, SCD
|
|
6. Модели данных. 3NF, Data Vault, Звезда
|
|
7. Практикум. Собираем витрину
|
|
8. Зачем разные модели (демо-«дерево решений»)
|
|
9. Эксплуатация. Качество и эволюция
|
|
10. Заключение
|
|
|
|
> Примечание про Mermaid: диаграммы `mindmap` и `quadrantChart` поддерживаются не всеми рендерами. Если планируете публикацию на площадке со строгим Markdown (например, Habr), оставьте текстовую альтернативу.
|
|
|
|
---
|
|
|
|
## **1. Введение: аналитика — это не оперативный учёт**
|
|
|
|
**OLTP (оперативный учёт)**: CRM, заказы, склад.
|
|
**DWH (аналитическое хранилище)**: единая модель для анализа.
|
|
|
|
Проблема: в OLTP для исторической аналитики — сложные JOIN, тяжёлые агрегации, нестабильная производительность.
|
|
|
|
**Особенности DWH**:
|
|
|
|
* Слоистая архитектура
|
|
* Оптимизированные под аналитику модели
|
|
|
|
**Ключевые тезисы**:
|
|
|
|
* OLTP vs OLAP: транзакции против анализа
|
|
* Почему «одна большая таблица» не работает на истории
|
|
* 3 преимущества слоёв: управляемость, производительность, прозрачность
|
|
|
|
---
|
|
|
|
## **2. Учебный пример: интернет-магазин**
|
|
|
|
ER-диаграмма сущностей источника (упрощённо):
|
|
|
|
```mermaid
|
|
erDiagram
|
|
CUSTOMER ||--o{ ORDER : places
|
|
ORDER ||--|{ ORDER_ITEM : contains
|
|
PRODUCT ||--o{ ORDER_ITEM : referenced
|
|
PRODUCT ||--o{ PRICE : has
|
|
PROMO ||--o{ ORDER_ITEM : applied
|
|
CUSTOMER {
|
|
int customer_id PK
|
|
string email
|
|
string phone
|
|
}
|
|
PRODUCT {
|
|
int product_id PK
|
|
string name
|
|
}
|
|
ORDER {
|
|
int order_id PK
|
|
date order_date
|
|
int customer_id FK
|
|
}
|
|
ORDER_ITEM {
|
|
int order_item_id PK
|
|
int order_id FK
|
|
int product_id FK
|
|
int qty
|
|
numeric price_at_sale
|
|
}
|
|
PRICE {
|
|
int product_id FK
|
|
date valid_from
|
|
date valid_to
|
|
numeric price
|
|
}
|
|
PROMO {
|
|
int promo_id PK
|
|
string code
|
|
}
|
|
```
|
|
|
|
---
|
|
|
|
## **3. Архитектура хранилища: зачем делить на слои?**
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
subgraph Sources[Источники OLTP]
|
|
A[CRM]
|
|
B[Заказы]
|
|
C[Склад]
|
|
end
|
|
|
|
A --> STG
|
|
B --> STG
|
|
C --> STG
|
|
|
|
subgraph STG[STG (Bronze) — сырые данные]
|
|
D[Таблицы-клоны \n неизменяемые/переигрузка]
|
|
end
|
|
|
|
STG --> ODS
|
|
|
|
subgraph ODS[ODS (Silver) — очищенные]
|
|
E[Типизация, валидация, \n стандартизация кодов]
|
|
end
|
|
|
|
ODS --> DDS
|
|
|
|
subgraph DDS[DDS — интегрированная модель]
|
|
F[Бизнес-сущности, SK/BK, SCD]
|
|
end
|
|
|
|
DDS --> DM
|
|
|
|
subgraph DM[DM (Gold) — витрины]
|
|
G[Звезда/Снежинка, агрегаты]
|
|
end
|
|
|
|
DM --> BI[BI/Отчёты]
|
|
```
|
|
|
|
---
|
|
|
|
## **4. Путешествие данных по слоям**
|
|
|
|
```mermaid
|
|
flowchart LR
|
|
subgraph STG[STG — сырьё]
|
|
A[raw_orders.json]
|
|
B[raw_customers.csv]
|
|
C[Формат как в источнике]
|
|
end
|
|
|
|
subgraph ODS[ODS — очистка]
|
|
D[stg_orders]
|
|
E[stg_customers]
|
|
F[Типизация, валидация]
|
|
end
|
|
|
|
subgraph DDS[DDS — интеграция]
|
|
G[dim_customer — SCD2]
|
|
H[fact_orders — с суррогатными ключами]
|
|
end
|
|
|
|
subgraph DM[DM — готовые решения]
|
|
I[mart_sales — агрегаты]
|
|
J[mart_customer_360]
|
|
end
|
|
|
|
STG --> ODS
|
|
ODS --> DDS
|
|
DDS --> DM
|
|
```
|
|
|
|
### 4.1 STG (Staging/Bronze)
|
|
|
|
Коротко: «как пришло». Идемпотентность, дедупликация, неизменяемость/переигрузка.
|
|
|
|
### 4.2 ODS (Operational Data Store/Silver)
|
|
|
|
Коротко: чистка и выравнивание типов, базовая унификация кодов, ещё без тяжёлой бизнес-логики.
|
|
|
|
### 4.3 DDS (Integrated/Conformed)
|
|
|
|
Коротко: интеграция источников, общие справочники, SK/BK, SCD. Здесь живут модели данных (3NF/DV/Anchor/Star).
|
|
|
|
### 4.4 DM (Data Marts/Gold)
|
|
|
|
Коротко: модели под задачи BI (звезда/снежинка). Агрегаты, материализации.
|
|
|
|
---
|
|
|
|
## **5. Базовые понятия: факты, измерения, ключи**
|
|
|
|
```mermaid
|
|
erDiagram
|
|
dim_date ||--o{ fact_sales : "дата"
|
|
dim_customer ||--o{ fact_sales : "клиент"
|
|
dim_product ||--o{ fact_sales : "товар"
|
|
|
|
dim_customer {
|
|
bigint customer_sk PK
|
|
varchar customer_bk
|
|
varchar customer_name
|
|
varchar email
|
|
date valid_from
|
|
date valid_to
|
|
boolean is_current
|
|
}
|
|
|
|
fact_sales {
|
|
bigint sale_id PK
|
|
bigint customer_sk FK
|
|
bigint product_sk FK
|
|
int date_sk FK
|
|
int quantity
|
|
decimal amount
|
|
}
|
|
```
|
|
|
|
**SCD Type 2 — визуализация истории:**
|
|
|
|
```mermaid
|
|
gantt
|
|
title SCD Type 2: История изменений клиента (ID = 123)
|
|
dateFormat YYYY-MM-DD
|
|
axisFormat %Y-%m
|
|
|
|
section Москва, premium@email.com
|
|
Версия 1 :active, 2023-01-01, 2023-05-15
|
|
|
|
section Москва, new_premium@email.com
|
|
Версия 2 :active, 2023-05-16, 2023-09-30
|
|
|
|
section Санкт-Петербург, new_premium@email.com
|
|
Версия 3 :active, 2023-10-01, 2024-12-31
|
|
```
|
|
|
|
---
|
|
|
|
## **6. Модели данных для DDS**
|
|
|
|
### 3NF (третья нормальная форма)
|
|
|
|
Классическая нормализованная модель, сильная целостность и интеграция, но SQL для аналитики сложнее.
|
|
|
|
### Снежинка (Snowflake)
|
|
|
|
Нормализованные измерения поверх звезды — компромисс между читаемостью и дублированием.
|
|
|
|
### Звезда (Star Schema)
|
|
|
|
Факт + денормализованные измерения — просто и быстро для BI/SQL.
|
|
|
|
### **Data Vault 2.0**
|
|
|
|
```mermaid
|
|
erDiagram
|
|
HUB_CUSTOMER ||--o{ SAT_CUSTOMER : has_history
|
|
HUB_ORDER ||--o{ SAT_ORDER : has_history
|
|
HUB_PRODUCT ||--o{ SAT_PRODUCT : has_history
|
|
|
|
HUB_CUSTOMER ||--o{ LNK_ORDER_CUSTOMER : participates
|
|
HUB_ORDER ||--o{ LNK_ORDER_CUSTOMER : includes
|
|
|
|
HUB_ORDER ||--o{ LNK_ORDER_PRODUCT : contains
|
|
HUB_PRODUCT ||--o{ LNK_ORDER_PRODUCT : involved
|
|
|
|
HUB_CUSTOMER {
|
|
string customer_hk PK
|
|
string customer_bk
|
|
datetime load_dts
|
|
string src
|
|
}
|
|
|
|
LNK_ORDER_PRODUCT {
|
|
string order_product_hk PK
|
|
string order_hk FK
|
|
string product_hk FK
|
|
datetime load_dts
|
|
string src
|
|
}
|
|
|
|
SAT_CUSTOMER {
|
|
string customer_hk PK,FK
|
|
datetime load_dts PK
|
|
string email
|
|
string phone
|
|
string city
|
|
hashdiff hash
|
|
}
|
|
```
|
|
|
|
Коротко: хабы (BK), линки (связи), сателлиты (история атрибутов). Этот раздел обзорный.
|
|
|
|
### Anchor Modeling (анкерное моделирование)
|
|
|
|
Атомарная декомпозиция сущностей и атрибутов, гибкая эволюция схемы; высокая гранулярность усложняет чтение.
|
|
|
|
> **Сравнение (интуитивно):** Звезда — проще/быстрее для BI; 3NF — целостность и интеграция; DV/Anchor — масштабируемая интеграция из многих источников и «история по умолчанию», но сложнее читать и писать.
|
|
|
|
---
|
|
|
|
## **7. Практикум: собираем витрину**
|
|
|
|
```mermaid
|
|
flowchart TD
|
|
A[Источники] --> STG
|
|
STG[STG: сырые заказы, товары] --> ODS
|
|
ODS[ODS: очищенные данные] --> DDS
|
|
|
|
subgraph DDS[DDS: интегрированная модель]
|
|
B[dim_customer (SCD2)]
|
|
C[dim_product]
|
|
D[dim_date]
|
|
E[fact_sales]
|
|
end
|
|
|
|
DDS --> F{Сборка витрины}
|
|
|
|
F --> G[mart_daily_sales]
|
|
F --> H[mart_customer_lifetime]
|
|
|
|
G --> I[Дашборд продаж]
|
|
H --> J[Отчёт по клиентам]
|
|
```
|
|
|
|
**Пример SQL для витрины:**
|
|
|
|
```sql
|
|
-- Витрина ежедневных продаж (пример)
|
|
CREATE TABLE mart_daily_sales AS
|
|
SELECT
|
|
d.date,
|
|
p.product_name,
|
|
c.customer_segment,
|
|
SUM(f.quantity) AS total_quantity,
|
|
SUM(f.amount) AS total_amount
|
|
FROM fact_sales f
|
|
JOIN dim_date d ON f.date_sk = d.date_sk
|
|
JOIN dim_product p ON f.product_sk = p.product_sk
|
|
JOIN dim_customer c ON f.customer_sk = c.customer_sk AND c.is_current = TRUE
|
|
GROUP BY d.date, p.product_name, c.customer_segment;
|
|
```
|
|
|
|
---
|
|
|
|
## **9. Эксплуатация: качество и эволюция**
|
|
|
|
```mermaid
|
|
graph TB
|
|
A[Данные] --> B{Контроль качества}
|
|
B --> C[Проверка уникальности]
|
|
B --> D[Проверка полноты]
|
|
B --> E[Валидация форматов]
|
|
B --> F[Свежесть данных]
|
|
C --> G[✅ Успех]
|
|
D --> G
|
|
E --> G
|
|
F --> G
|
|
C --> H[❌ Ошибка]
|
|
D --> H
|
|
E --> H
|
|
F --> H
|
|
G --> I[Загрузка в слой]
|
|
H --> J[Оповещение и остановка]
|
|
```
|
|
|
|
---
|
|
|
|
## **10. Заключение: главное — понимать «почему»**
|
|
|
|
Ключевые идеи: разделение на слои (STG → ODS → DDS → DM), разные модели для разных задач (3NF, Star, DV, Anchor), факты/измерения/SCD, итеративная сборка витрин под конкретные вопросы бизнеса.
|
|
|
|
---
|
|
|
|
## ЧАСТЬ B. Наши предложения к статье (что усилено и что добавить)
|
|
|
|
Ниже — блоки, которые можно встроить в соответствующие разделы или вынести в отдельные боксы/врезки.
|
|
|
|
### 1) Рамочный ввод «что вы знаете / что узнаете / чего не будет»
|
|
|
|
* **Уже умеете:** SQL-основы (SELECT/JOIN/GROUP BY), простые агрегаты.
|
|
* **Узнаете:** слои (STG/ODS/DDS/DM), модели (3NF/Star/DV/Anchor), SCD и суррогатные ключи.
|
|
* **Не рассматриваем здесь:** физический дизайн, производительность, партиционирование, распределённые кластеры — это отдельная тема.
|
|
|
|
### 2) Мини-глоссарий RU/EN (по 1–2 строки)
|
|
|
|
* **Слой (Layer)** — логический уровень в DWH: STG/ODS/DDS/DM.
|
|
* **Витрина (Data Mart)** — предметно-ориентированный набор таблиц/представлений для конкретной аналитики.
|
|
* **Факт (Fact)** — таблица событий/измерений величин (кол-во, сумма).
|
|
* **Измерение (Dimension)** — справочник контекста фактов (клиенты, товары, даты).
|
|
* **Суррогатный ключ (Surrogate Key, SK)** — искусственный технический ключ (int/bigint).
|
|
* **Бизнес-ключ (Business Key, BK)** — естественный ключ из источника (например, `customer_id`).
|
|
* **SCD (Slowly Changing Dimension)** — подход к хранению истории атрибутов измерения.
|
|
* **CDC (Change Data Capture)** — техника инкрементальной загрузки изменений.
|
|
* **Conformed Dimension** — «конформное» измерение, общее для нескольких витрин.
|
|
|
|
### 3) Отображение синонимов слоёв
|
|
|
|
* **STG (Bronze)** → **ODS (Silver)** → **DDS (Conformed/Core)** → **DM (Gold)**.
|
|
|
|
### 4) «Правила слоя» — короткие чек-листы
|
|
|
|
* **STG (Bronze):** как пришло; неизменяемо/переигрузка; идемпотентность; дедуп по `(BK, load_ts)`; только тех. обогащения (например, `_ingest_id`).
|
|
* **ODS (Silver):** типизация; базовая очистка и стандартные коды; **без** тяжёлой бизнес-логики; стабильные схемы/имена.
|
|
* **DDS (Conformed/Core):** интеграция источников; единая терминология; SK/BK; SCD; здесь живут модели (3NF/Star/DV/Anchor).
|
|
* **DM (Gold):** под конкретные вопросы BI/продукта; агрегаты/материализации; доступные метрики и измерения.
|
|
|
|
### 5) Антипаттерны (короткий бокс)
|
|
|
|
* «Одна огромная историческая таблица» → медленные запросы, нет истории атрибутов.
|
|
* «Смешали STG и ODS» → потеря трассировки ошибок и инцидентов качества.
|
|
* «Факт с текстовыми атрибутами без причин» → раздутая таблица и неявные бизнес-правила.
|
|
* «SCD без BK» → история «плывёт», не привязана к бизнес-идентификатору.
|
|
|
|
### 6) SCD2: рецепт джойна «по интервалу» (шпаргалка)
|
|
|
|
```sql
|
|
-- Привязка факта к актуальной версии измерения (Type 2)
|
|
SELECT f.*, d.customer_sk
|
|
FROM fact_sales f
|
|
JOIN dim_customer d
|
|
ON d.customer_bk = f.customer_bk -- совпадение BK
|
|
AND f.event_date >= d.valid_from
|
|
AND f.event_date < COALESCE(d.valid_to, DATE '2999-12-31');
|
|
```
|
|
|
|
### 7) Мини-практикум (сквозной, короткий)
|
|
|
|
1. Показать джойн факта к SCD2-измерению (по примеру выше).
|
|
2. Собрать `mart_daily_sales` (у вас уже есть SQL).
|
|
3. Пара проверок качества (см. ниже).
|
|
|
|
### 8) Качество данных: минимальные метрики + однострочники
|
|
|
|
* **Freshness (свежесть):** `MAX(event_time)` против «сейчас».
|
|
* **Completeness (полнота):** доля NULL по важным столбцам.
|
|
* **Consistency (согласованность):** факт без соответствия в измерении (нарушение покрытии ссылок).
|
|
* **Uniqueness (уникальность):** уникальность BK в справочнике.
|
|
|
|
```sql
|
|
-- Примеры «однострочников»
|
|
-- Freshness
|
|
SELECT NOW() - MAX(event_time) AS lag FROM ods.order;
|
|
|
|
-- Uniqueness BK
|
|
SELECT customer_bk, COUNT(*)
|
|
FROM dds.dim_customer
|
|
GROUP BY customer_bk
|
|
HAVING COUNT(*) > 1;
|
|
|
|
-- Orphan facts (без покрытия измерения)
|
|
SELECT COUNT(*)
|
|
FROM dds.fact_sales f
|
|
LEFT JOIN dds.dim_customer d ON f.customer_sk = d.customer_sk
|
|
WHERE d.customer_sk IS NULL;
|
|
```
|
|
|
|
### 9) Нейминг-гайд (пример)
|
|
|
|
* `stg.*` — как в источнике, добавочные тех. поля: `_ingest_id`, `_load_ts`.
|
|
* `ods.*` — очищенные «плоские» таблицы, стаб. схемы.
|
|
* `dds.dim_*`, `dds.fact_*` — интегрированная модель.
|
|
* `dm.mart_*` — витрины/представления.
|
|
|
|
### 10) «Зачем разные модели» (без выбора)
|
|
|
|
* **Star (звезда):** быстро писать отчёты, учим новичков на ней.
|
|
* **3NF:** лучшая консистентность/интеграция понятий, тяжелее для BI.
|
|
* **Data Vault:** масштабируемая интеграция множества источников + «история по умолчанию».
|
|
* **Anchor:** гибкая эволюция схемы, максимальная атомарность, цена — сложность.
|
|
|
|
### 11) Что почитать дальше
|
|
|
|
* **Kimball, The Data Warehouse Toolkit** — модели «звезды», SCD.
|
|
* **Inmon, Building the Data Warehouse** — корпоративный DWH и нормализация.
|
|
* **Linstedt & Olschimke, The Data Vault 2.0** — практический DV.
|
|
* **Anchor Modeling** — официальный сайт/документация по Anchor.
|
|
|
|
---
|
|
|
|
## ЧАСТЬ C. Мини-датасет (для примеров в статье)
|
|
|
|
> Опционально приложить к репозиторию/приложению статьи.
|
|
|
|
**customers.csv**
|
|
|
|
```
|
|
customer_id,email,phone,city
|
|
101,a@ex.com,700,Москва
|
|
101,b@ex.com,700,Москва
|
|
102,c@ex.com,701,СПб
|
|
```
|
|
|
|
**orders.csv**
|
|
|
|
```
|
|
order_id,order_date,customer_id
|
|
5001,2024-01-10,101
|
|
5002,2024-02-05,102
|
|
```
|
|
|
|
**order_items.csv**
|
|
|
|
```
|
|
order_item_id,order_id,product_id,qty,price_at_sale
|
|
1,5001,9001,2,100.00
|
|
2,5001,9002,1,50.00
|
|
3,5002,9001,1,100.00
|
|
```
|
|
|
|
**products.csv**
|
|
|
|
```
|
|
product_id,name
|
|
9001,Phone
|
|
9002,Case
|
|
```
|
|
|
|
**prices.csv**
|
|
|
|
```
|
|
product_id,valid_from,valid_to,price
|
|
9001,2023-12-01,2024-01-31,100
|
|
9001,2024-02-01,2999-12-31,110
|
|
```
|
|
|
|
---
|
|
|
|
## ЧАСТЬ D. DDL-скелеты (минимально)
|
|
|
|
```sql
|
|
-- DDS: измерение клиента (Type 2)
|
|
CREATE TABLE dds.dim_customer (
|
|
customer_sk BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
customer_bk VARCHAR NOT NULL,
|
|
email VARCHAR,
|
|
phone VARCHAR,
|
|
city VARCHAR,
|
|
valid_from DATE NOT NULL,
|
|
valid_to DATE,
|
|
is_current BOOLEAN NOT NULL DEFAULT TRUE
|
|
);
|
|
|
|
-- DDS: факт продаж (гранулярность: строка заказа)
|
|
CREATE TABLE dds.fact_sales (
|
|
sale_id BIGINT GENERATED ALWAYS AS IDENTITY PRIMARY KEY,
|
|
customer_sk BIGINT NOT NULL,
|
|
product_sk BIGINT NOT NULL,
|
|
date_sk INT NOT NULL,
|
|
quantity INT NOT NULL,
|
|
amount DECIMAL(18,2) NOT NULL
|
|
);
|
|
```
|
|
|
|
---
|
|
|
|
## ЧАСТЬ E. Текстовые альтернативы для «капризных» диаграмм
|
|
|
|
Если `mindmap`/`quadrantChart` не поддерживаются местом публикации, используйте пункты:
|
|
|
|
* **Сводка:**
|
|
|
|
* Архитектура: слои STG→ODS→DDS→DM, разделение ответственности, управляемость изменений.
|
|
* Модели: 3NF (целостность), Data Vault (масштабируемость), Звезда (производительность), Anchor (гибкая эволюция).
|
|
* Понятия: факты, измерения, SCD2, SK/BK.
|
|
* Практика: понимать бизнес-задачу, выбирать подход под контекст, строить итеративно.
|
|
|
|
* **Интуитивное сравнение моделей (простыми словами):**
|
|
Звезда — «быстро стартануть BI»; 3NF — «собрать единый словарь сущностей»; DV/Anchor — «надёжно интегрировать десятки источников и хранить всю историю», но читать сложнее.
|
|
|
|
---
|
|
|
|
## Итог
|
|
|
|
Статья даёт общий словарь и картинку мира: что за слои в DWH, какие модели бывают и зачем они нужны. Задача менти — почувствовать интуицию различий, научиться видеть место фактов/измерений/SCD и уверенно собрать простую витрину из DDS. Дальше — углубляться по нужной модели в отдельном материале (звезда/DV/Anchor/3NF).
|