DWH, витрина данных и data lake: что это и чем они отличаются
DWH — хранилище данных для анализа, витрина — готовая таблица под конкретную задачу, data lake — хранилище сырых файлов. Чем DWH отличается от базы приложения, из каких слоёв состоит, как выглядит витрина на SQL и почему в двух витринах бывают разные цифры.
Содержание статьи
На совещании два дашборда показывают выручку за август. В одном 15 788 долларов, в другом 12 595. Оба «из хранилища», оба построены аналитиками, и оба посчитаны правильно — просто это разные метрики с одинаковой подписью. Чтобы разобраться, откуда такое берётся, нужно понимать, как устроено место, где живут аналитические данные. DWH (data warehouse, хранилище данных) — это отдельная база, куда данные из разных систем компании собирают, очищают и хранят с историей, чтобы их можно было анализировать. Витрина данных — готовая таблица внутри хранилища, собранная под конкретную задачу или команду. Data lake (озеро данных) — хранилище сырых файлов любого формата, которые складывают как есть и разбирают при чтении.
Коротко
- DWH — аналитическая база, в которой собраны данные из приложения, биллинга, CRM и других источников в согласованном виде и с историей.
- Витрина данных — таблица под конкретный вопрос: дневные метрики продукта, выручка по каналам, воронка онбординга.
- Data lake хранит сырые файлы дёшево и в любом формате, но структуру приходится разбирать при каждом чтении.
- Хранилище обычно делят на слои: сырые данные, очищенный детальный слой и витрины.
- Разные цифры в двух витринах чаще всего означают разные определения метрики, а не ошибку в SQL.
Что такое DWH (хранилище данных) простыми словами
Хранилище данных — это база, созданная не для работы продукта, а для ответов на вопросы о нём. Приложение пишет события и заказы в свою базу, биллинг — оплаты в свою, CRM — сделки в свою. В хранилище эти данные приезжают по расписанию через ETL-процесс, приводятся к единым типам и ключам и лежат рядом, поэтому их можно соединять в одном запросе.
Второе свойство DWH — история. База приложения хранит текущее состояние: тариф пользователя сейчас. Хранилище может хранить, каким тариф был в каждый момент, и поэтому отвечает на вопросы вроде «сколько было платящих на конец июля».
Учебная база SQL-курса — маленькая модель такого хранилища. В ней пять таблиц и 45 185 строк: пользователи, события приложения, оплаты, подписки и попадания в эксперименты. В реальной компании эти данные пришли бы из трёх-четырёх разных систем.
Чем хранилище отличается от базы приложения
База приложения рассчитана на много коротких операций: записать событие, найти пользователя по id, списать деньги. Такие системы называют OLTP. Хранилище рассчитано на тяжёлые запросы по миллионам строк: посчитать DAU за квартал, сравнить когорты. Это OLAP-нагрузка. Подробно разница разобрана в материале про OLAP и OLTP, ссылка в конце.
Главная практическая причина не считать аналитику прямо в базе приложения — она мешает продукту. Тяжёлый запрос аналитика может замедлить приложение для пользователей. Вторая причина — в базе приложения нет данных других систем, и выручку по каналам привлечения там просто не с чем соединить.
| Что сравниваем | База приложения (OLTP) | Хранилище (DWH) |
|---|---|---|
| Зачем | чтобы продукт работал | чтобы отвечать на вопросы о продукте и бизнесе |
| Типичный запрос | найти одну строку, записать одну строку | агрегировать миллионы строк за период |
| Источники | одна система | много систем, сведённых по ключам |
| История | в основном текущее состояние | история изменений и снимки |
| Кто пишет | приложение | конвейеры загрузки (ETL/ELT) |
Из каких слоёв состоит хранилище
Названия слоёв в компаниях разные, но логика почти всегда одна: данные проходят путь от сырых к готовым, и на каждом шаге их становится меньше, а смысла больше.
Сырой слой (raw, staging) хранит данные так, как их отдал источник, иногда с минимальной чисткой. К нему почти не ходят с аналитическими запросами, но он нужен, чтобы пересчитать всё заново, если в логике найдут ошибку.
Детальный слой (core, detail, иногда ODS или DDS) — очищенные таблицы на уровне отдельных фактов: одно событие, одна оплата, один пользователь. Здесь уже нет дублей, типы согласованы, ключи связывают таблицы. Таблицы users, events и payments учебной базы похожи именно на этот слой.
Слой витрин (marts) — агрегаты и широкие таблицы под конкретные задачи. Дашборды и отчёты читают в основном отсюда.
| Слой | Что лежит | Пример | Кто читает |
|---|---|---|---|
| Сырой (raw, staging) | данные как пришли из источника | JSON событий из приложения, выгрузка оплат из биллинга | дата-инженеры |
| Детальный (core, detail) | очищенные факты и справочники | events, payments, users | аналитики, для новых вопросов |
| Витрины (marts) | агрегаты и таблицы под задачу | дневные метрики продукта, выручка по каналам | дашборды, менеджеры, аналитики |
Что такое витрина данных
Витрина данных (data mart) — таблица, собранная под конкретный вопрос или команду, чтобы его не приходилось каждый раз собирать с нуля. Если продакт каждое утро смотрит DAU, регистрации и выручку по дням, логично один раз свести их в таблицу «одна строка — один день» и строить дашборд на ней.
Ниже такая витрина, собранная одним запросом на учебной базе. Каждый CTE считает показатель из своего источника: активность — из событий, регистрации — из пользователей, выручку и новых плательщиков — из оплат. Затем всё соединяется по дню. В хранилище результат сохраняли бы как таблицу и обновляли после каждой загрузки.
| day | dau | signups | new_payers | revenue |
|---|---|---|---|---|
| 2026-08-24 | 484 | 85 | 13 | 529 |
| 2026-08-25 | 529 | 86 | 16 | 660 |
| 2026-08-26 | 568 | 87 | 13 | 519 |
| 2026-08-27 | 556 | 87 | 14 | 604 |
| 2026-08-28 | 586 | 88 | 12 | 646 |
| 2026-08-29 | 530 | 38 | 11 | 607 |
| 2026-08-30 | 223 | 0 | 20 | 721 |
with days as (
select distinct event_time::date as day from events
),
activity as (
select event_time::date as day, count(distinct user_id) as dau
from events
group by 1
),
signups as (
select signup_date as day, count(*) as signups
from users
group by 1
),
revenue as (
select
paid_at as day,
sum(amount) as revenue,
count(*) filter (where payment_type = 'first') as new_payers
from payments
group by 1
)
select
d.day,
a.dau,
coalesce(s.signups, 0) as signups,
coalesce(r.new_payers, 0) as new_payers,
coalesce(r.revenue, 0) as revenue
from days as d
left join activity as a on a.day = d.day
left join signups as s on s.day = d.day
left join revenue as r on r.day = d.day
where d.day between date '2026-08-24' and date '2026-08-30'
order by d.day;Почему последняя строка витрины врёт
Посмотрите на 30 августа: DAU упал до 223, регистраций ноль, а выручка максимальная за неделю. Продукт тут ни при чём. Выгрузку закрыли в 13:00, поэтому события есть только за первую половину дня. В таблице пользователей регистрации заканчиваются 29 августа, и витрина честно ставит ноль. Оплаты хранятся с точностью до даты, и по ним не видно, полный ли день.
Витрина свела в одну строку три источника с разной свежестью. Каждая цифра в ней правильная для своей таблицы, а вместе они рассказывают несуществующую историю. Хорошая витрина хранит рядом с данными отметку, до какого момента она полна, а дашборд не показывает дни после этой отметки или помечает их как неполные.
coalesce(s.signups, 0) удобен для графиков, но превращает «регистрации не загружены» в «регистраций не было». Если источник мог не доехать, оставляйте NULL и проверяйте свежесть каждого источника перед обновлением витрины.
Факты и измерения: схема «звезда»
Детальный слой часто строят по схеме «звезда». В центре — таблицы фактов: каждая строка фиксирует событие или операцию с числами, которые можно суммировать. Вокруг — таблицы измерений: справочники, по которым факты режут.
В учебной базе events (35 341 строка) и payments (1251 строка) — факты. users (4613 строк) — измерение: у каждого пользователя есть канал привлечения, страна и устройство. Запрос «выручка по каналам» — типичный запрос к звезде: взять факт, присоединить измерение, сгруппировать по его атрибуту.
| channel | payments | revenue |
|---|---|---|
| organic | 283 | 7087 |
| referral | 198 | 4792 |
| partner | 90 | 2200 |
| paid_search | 71 | 1709 |
select
u.channel,
count(*) as payments,
sum(p.amount) as revenue
from payments as p
join users as u on u.user_id = p.user_id
where p.paid_at between date '2026-08-01' and date '2026-08-31'
group by u.channel
order by revenue desc;Data lake, DWH и lakehouse: в чём разница
Data lake — хранилище файлов: логи, JSON событий, выгрузки, картинки, файлы в колоночных форматах. Файлы складывают как есть, а структуру описывают в момент чтения (schema on read). Это дёшево и гибко: можно сохранить данные, даже если пока непонятно, как их использовать. Обратная сторона — без дисциплины озеро превращается в свалку файлов, которые никто не может найти и прочитать.
DWH хранит таблицы с заранее описанной структурой (schema on write): данные проверяют и приводят к схеме при загрузке. Писать в него сложнее, зато читать просто, и аналитики работают с ним на SQL.
Lakehouse — подход, в котором поверх файлов озера добавляют табличный слой: схему, транзакции, версии данных. Так к файлам можно обращаться почти как к таблицам хранилища. Это не отдельный продукт, а архитектурный выбор, и многие компании живут без него.
| Что сравниваем | Data lake | DWH | Lakehouse |
|---|---|---|---|
| Что хранит | файлы любого формата | таблицы | файлы с табличным слоем поверх |
| Когда описана структура | при чтении | при загрузке | при записи в таблицы поверх файлов |
| Кто в основном работает | дата-инженеры, ML-команды | аналитики, BI | и те, и другие |
| Сильная сторона | дёшево хранить всё, в том числе сырьё | согласованные данные, быстрый SQL | одна копия данных для аналитики и ML |
| Типичная проблема | сложно найти и доверять данным | дорого менять структуру | сложнее в настройке и сопровождении |
Кто за что отвечает: дата-инженер и аналитик
Дата-инженер отвечает за то, чтобы данные доезжали: конвейеры загрузки, сырой и детальный слои, расписание, мониторинг, права доступа. Если данные за вчера не пришли, это его зона.
Аналитик отвечает за смысл: какие витрины нужны, как определены метрики, правильно ли их читают. Он первым замечает, что цифра странная, и должен уметь отличить поломку данных от изменения в продукте.
Между ними часто появляется роль analytics engineer: человек, который пишет SQL-модели детального слоя и витрин, покрывает их тестами и документирует определения. В небольших командах всё это делает один аналитик, и тогда особенно важно, чтобы логика витрин лежала в коде, а не в головах.
Почему в двух витринах разные цифры
Вернёмся к двум «выручкам» из начала. Первая витрина суммирует оплаты, прошедшие в августе: 15 788 долларов. Вторая суммирует месячную цену всех активных подписок: 12 595 долларов. Первая показывает деньги, которые пришли за месяц. Вторая ближе к MRR — регулярному доходу, который приносят подписки на текущий момент. Обе цифры верны, но подпись «выручка» на обеих вводит в заблуждение.
С активностью то же самое. 28 августа хотя бы одно событие сделали 586 пользователей. Если считать активными только тех, кто сделал ключевое действие, то есть что-то кроме открытия приложения, их 91. Витрина маркетинга и витрина продукта могут называть DAU разные вещи, и никто этого не заметит, пока цифры не положат рядом.
select
(select sum(amount)
from payments
where paid_at between date '2026-08-01' and date '2026-08-31') as payments_august,
(select sum(monthly_price)
from subscriptions
where status = 'active') as active_subscriptions_monthly;- Держите определение каждой метрики в одном месте: одна модель или витрина, из которой читают все дашборды.
- Называйте колонки по смыслу:
payments_augustиactive_subscriptions_monthly, а не две колонкиrevenue. - У каждой витрины должен быть владелец, который отвечает на вопрос «как это посчитано».
- Храните отметку полноты данных и не показывайте незакрытый день как обычный.
Что читать дальше
Как данные попадают в хранилище, разобрано в материале про ETL. Почему одна и та же метрика даёт разные ответы, — в разборе ошибок чтения метрик.
Материалы по теме

ETL: что это простыми словами, этапы и чем ETL отличается от ELT
ETL — процесс, который забирает данные из источников, приводит их в порядок и загружает в хранилище. Этапы extract, transform, load на примере продукта, разница ETL и ELT, инкрементальная загрузка, идемпотентность и SQL-проверки качества данных.

OLAP и OLTP: в чём разница простыми словами
OLTP — системы, которые обслуживают работу продукта мелкими транзакциями, OLAP — системы для тяжёлых аналитических запросов. Чем они отличаются по запросам, схеме и хранению, почему отчёты не строят на рабочей базе и что такое OLAP-куб.

MRR и ARR: что это, как считать и что они скрывают
MRR — регулярная выручка активных подписок, приведённая к одному месяцу, ARR — та же величина в годовом масштабе. Как посчитать MRR в SQL, что делать с просроченными оплатами, чем MRR отличается от денег за месяц и из чего складывается его изменение.