TGViewer
P(hD)ython P(hD)ython @phdython · 332 subscribers
Post #60 482
«Data Lake: от перестановки мест слагаемых сумма... меняется? 👷»

#system_design

Недавно проводил лекцию по DWH на курсе System Design от nevzorov.courses.

На лекции разбирали довольно частый практический кейс:
- есть ряд поддерживаемых источников данных (Sources);
- есть множество клиентов (Customers);
- для каждого клиента необходимо сохранять и обрабатывать данные из его источников (Customer Sources);
- вопрос: как лучше спроектировать Data Lake под эту задачу?


Вариант 1: customers/<customer_name>/source=<source_name>
Вариант 2: sources/<source_name>/customer=<customer_name>

Интуитивно рука тянется к 1 варианту... Однако для Data Lake и дальнейшей DWH-инфраструктуры часто лучше именно 2 вариант:

raw/sources/<source_name>/customer=<customer_name>/...
cleaned/sources/<source_name>/customer=<customer_name>/...
...


Например:

...
raw/sources/google_play/customer=rammstein/dt=2026-05-24/*.parquet
raw/sources/google_play/customer=sabaton/dt=2026-05-24/*.parquet
raw/sources/google_play/customer=megadeth/dt=2026-05-24/*.parquet
...
raw/sources/trustpilot/customer=rammstein/dt=2026-05-24/*.parquet
raw/sources/trustpilot/customer=led_zeppelin/dt=2026-05-24/*.parquet
raw/sources/trustpilot/customer=lordi/dt=2026-05-24/*.parquet
...


Почему?

1. Source естественным образом превращается в таблицу.

Для AWS Athena, Apache Trino или Apache Spark - google-play, trustpilot и т.д. - это отдельные логические таблицы, разложенные по Parquet-файлам и партициям в виде Customer'ов:

SELECT
*
FROM
"raw"."google_play"
WHERE
("customer" = 'rammstein') AND ("dt" >= DATE '2026-05-01');


У google-play даже в сыром виде (и уж тем более в очищенном) есть какая-то своя схема данных, ключи, timestamp'ы, правила дедупликации, SLA, логика инкрементальной загрузки и т.д.
У trustpilot и любого другого Source'а - свои.

Если же сделать наоборот:

...
raw/customers/rammstein/source=google_play/dt=2026-05-24/*.parquet
raw/customers/rammstein/source=trustpilot/dt=2026-05-24/*.parquet
...
raw/customers/sabaton/source=google_play/dt=2026-05-24/*.parquet
...


то один логический источник google-play размазывается по разным корням. А дальше начинается адъ 👹:
- отдельные таблицы на каждый Source каждого Customer'а;
- UNION ALL запросы и VIEW-шки;
- бардак с Data Governance.

В общем, Data Lake, а следом за ним и DWH медленно, но неотвратимо превращаются в DataSwamp 😄

2. Data Mesh проще делать именно по Source'ам.

Естественная единица владения - это не «папка клиента» (Customer), а доменный источник (Source).
У каждого такого Source'а есть отдельная команда-владелец, контракты, документация, SLA, data quality checks и правила эволюции схемы.

Команда, отвечающая за google_play, должна владеть одной папкой sources/google_play/customer=<customer_name>/*, а не тысячами подпапок customers/*/source=google_play/*.

- Добавили нового клиента? Добавили новую партицию.
- Поменяли контракт источника? Обновили один data product.
- Поймали баг в ingestion? Чиним одний единственный ETL-pipeline.

3. Pipeline'ы обычно тоже мыслят именно Source'ами:

...
google_play
trustpilot
...


А не:

...
ingest_rammstein_everything
ingest_sabaton_everything
ingest_led_zeppelin_everything
...


Иначе очень быстро появляются «особые клиенты»:
- у этого legacy CSV;
- у этого timezone в строке;
- у этого timestamp иногда null;
- у этого producer шлёт дубликаты;
- у этого «ну вы там руками поправьте, пожалуйста».

Поздравляю, у вас не DWH, а зоопарк с Airflow DAG-ами 🦓

4. Наконец, Source-First Layout упрощает сложную аналитику:

SELECT
"customer", count(*)
FROM
"raw"."google_play"
WHERE
"dt" = DATE '2026-05-24'
GROUP BY
"customer";


Можно с лёгкостью строить Usage-Based Billing по конкретным Source'ам, позволять даже менеджменту без труда копаться в данных и т.д.

Таким образом, проектируя DWH-систему лучше думать не о том, какие у вас будут клиенты, а о том, какие источники данных вы будете для них поддерживать.

С уважением,
Михаил Масягин
  • ⚡ 4
  • 🔥 3
  • 🤔 2
  • 💯 2
More from @phdython
  1. Aug 25, 2026«Конференции и паровозики 🚂» #заметки_с_полей Отгремела «Профсоюзная 2.0» - конфа прошла…
  2. Aug 22, 2026Post #81
  3. Aug 20, 2026«Профсоюз выдвинул 😎» #заметки_с_полей 22 августа в Москве буду спикером на «Профсоюзной…
  4. Aug 16, 2026«Ну вот, я и стал админом 🧑‍💻» #техно_и_хардкор Собрал, казалось бы, банальный, но для м…
  5. Aug 11, 2026«Предзащита: больно и неофициально 🎓» #аспирантские_будни Наконец дошли руки до обещанног…
  6. Jul 18, 2026«Work-life balance от Anthropic ⚒️» #ai4sdlc Недельные лимиты токенов израсходованы - можн…
Threads Profile ViewerView any public Threads profile without an account.Open ThreadLook →Writing with AI? Make it sound human.Metric37 rewrites AI drafts so they read naturally. Free AI detector, 1,500 words free.Try Metric37 →