Прямой ответ
Data lineage (происхождение данных) — зафиксированная цепочка: откуда данные пришли, через какие преобразования прошли и где используются. Это ответ на два практических вопроса: «почему в отчёте такая цифра» и «что сломается, если я изменю эту таблицу».
Уровни детализации:
- Табличный (dataset-level). Связи между таблицами и задачами:
orders_raw → orders_curated → mart_daily_sales. Дешёв в реализации, достаточен для оценки влияния. - Колоночный (column-level). Связи между полями:
mart_daily_sales.revenue ← sum(orders_curated.total). Нужен для аудита определений метрик и разбора расхождений. - Уровень значений (value-level). Отслеживание конкретных записей через цепочку. Применяется редко, в основном в задачах трассируемости и расследований.
Источники информации о lineage:
- Оркестратор. DAG в Airflow даёт связи между задачами и расписание; сам по себе он не знает структуры таблиц, но задаёт порядок исполнения.
- Трансформационный слой. dbt строит граф моделей из SQL и
ref(), поэтому lineage появляется «бесплатно» вместе с документацией и тестами. - Парсер SQL. Разбор запросов позволяет вывести колоночные связи даже для кода, который не описан декларативно.
- Журналы СУБД и запросов.
pg_stat_statements,system.query_logв ClickHouse показывают, кто и какие таблицы читает — это lineage потребления.
Практическая ценность:
- Оценка влияния. Перед изменением схемы видно, какие витрины и отчёты затронуты.
- Разбор инцидентов. Путь от неверной цифры в дашборде к конкретной загрузке занимает минуты вместо дней.
- Аудит и регуляторика. Для персональных данных важно показать, где они обрабатываются и куда передаются.
- Борьба с «теневыми» данными. Если таблицу никто не читает, её можно вывести из эксплуатации; если читает неизвестный процесс — это риск.
Практика: минимально работающая система lineage
Шаг 1. Декларативные связи в коде. Все трансформации — в dbt-моделях с ref(), а не в ручных скриптах. Это даёт граф моделей и тесты — см. проверки качества данных.
Шаг 2. Метаданные в репозитории. Для каждой модели фиксируем: владельца, источник, назначение, SLA по свежести. Пример описания:
models:
- name: mart_daily_sales
description: "Ежедневная выручка по регионам; источник — orders_curated"
meta:
owner: data-team
sla_freshness: "08:00 MSK"
upstream: [orders_curated, dim_customer]
downstream_consumers: [finance_report, marketing_dashboard]
Шаг 3. Журнал загрузок. Каждая загрузка пишет строку в служебную таблицу: идентификатор запуска, период данных, число строк, длительность, версия кода. Это «доказательная база» при разборе расхождений.
CREATE TABLE etl_run_log (
run_id TEXT PRIMARY KEY,
model_name TEXT NOT NULL,
data_date DATE NOT NULL,
rows_loaded BIGINT,
started_at TIMESTAMPTZ,
finished_at TIMESTAMPTZ,
code_version TEXT
);
Шаг 4. Картография потребления. Регулярно собирайте список читателей таблиц из журналов СУБД и сверяйте с известными потребителями. Неизвестный читатель — повод разобраться, а не игнорировать.
Рекомендации:
- Начинайте с табличного уровня. Колоночный lineage дорог в поддержке; внедряйте его для критичных метрик, а не для всего хранилища.
- Не стройте lineage «вручную» в вики. Документация, которую не генерирует код, устаревает за месяц и начинает вводить в заблуждение.
- Связывайте lineage с проверками. Если известно, что витрина зависит от трёх загрузок, то падение любой из них должно блокировать публикацию витрины.
- Фиксируйте определения метрик. Расхождения между отчётами почти всегда про разные определения, а не про разные данные.
Практический сценарий: цифра в дашборде не сходится с финансовым отчётом
Контекст: маркетинг видит выручку на 3 % выше, чем финансы. Раньше такой разбор занимал два дня переписки.
- Идём по lineage от дашборда. Витрина
marketing_dashboardстроится изmart_marketing_sales, которая читаетorders_curatedбез фильтра по статусу. - Сравниваем с финансовым путём.
finance_reportиспользуетmart_daily_sales, где применён фильтрstatus IN ('paid','shipped'). - Локализация. Расхождение дают заказы в статусе
cancelled, которые попали в маркетинговую витрину. Это не ошибка данных, а разные определения меры. - Решение. Фиксируем единое определение выручки в семантическом слое; обе витрины переводим на общую меру; в описании моделей указываем, какие статусы входят в расчёт.
- Профилактика. Добавляем в CI проверку: витрины, использующие одну меру, дают одинаковый результат на контрольном периоде. Такие сверки — часть набора проверок качества.
Результат: расхождение устранено за два часа, причина задокументирована. Вывод: lineage ускоряет не «поиск таблиц», а согласование определений — именно там живёт большинство расхождений.
FAQ
Нужен ли отдельный инструмент для lineage? На старте достаточно графа dbt и журнала загрузок. Специализированные каталоги данных оправданы, когда хранилище обслуживает несколько команд, появляются нетривиальные потребители и требуется колоночный уровень с поиском по метаданным.
Как поддерживать lineage при ручных SQL-скриптах? Либо переводить скрипты в декларативную форму, либо извлекать связи парсером SQL из журналов выполнения. Второй вариант даёт неполную картину: он не знает намерения, только фактические обращения.
Что делать с lineage в потоковых пайплайнах? Фиксируйте связи «топик → таблица» и версии схем сообщений. При изменении схемы важно видеть, какие потребители затронуты; правила совместимости разобраны в статье про schema registry.
Как lineage связан с GDPR и удалением данных? Lineage показывает, в какие таблицы и витрины попали персональные данные, то есть где именно нужно выполнять удаление или обезличивание. Без карты происхождения запрос на удаление превращается в поиск по всему хранилищу с высоким риском пропустить копию.
Источники: Airflow docs, dbt docs, Wikipedia (data lineage) — ссылки в шапке.