A B C D E F G H I J K L M N O P Q R S T V W Y Z А Б В Г Е И К М О П С Т Ц

dbt

dbt

dbt (data build tool) — это SQL-first инструмент трансформации данных, который берёт на себя букву T в подходе ELT (Extract, Load, Transform), то есть слой между загрузкой сырых данных в хранилище и их подачей в BI (Business Intelligence), инструменты бизнес-аналитики. Вместо отдельного кластера для трансформаций dbt компилирует SQL-модели в обычные запросы и выполняет их прямо внутри хранилища данных (data warehouse, DWH), а взамен даёт версионирование через git, автоматические тесты и граф зависимостей моделей.

 

Что такое dbt

До появления dbt трансформация сырых таблиц в аналитические витрины обычно жила в двух плохо стыкующихся местах. Часть логики оседала в громоздких пайплайнах ETL (Extract, Transform, Load) на Python или Spark, часть превращалась в SQL-скрипты, которые кто-то запускал руками и не проверял версию. dbt Labs, компания-разработчик инструмента, предложила третий путь. Аналитик или дата-инженер пишет обычный SQL-запрос с обёрткой Jinja, кладёт его в файл модели, а dbt сам разбирается, в каком порядке запускать модели, как их материализовать и какие проверки прогнать после сборки.

Инструмент не хранит данные и не заменяет хранилище, он подключается к уже существующему DWH через адаптер и выполняет там DDL и DML, поэтому все ограничения по объёму и скорости задаёт само хранилище, а не dbt. Открытая версия называется dbt Core, устанавливается через pip и работает как CLI.

 

Архитектура dbt

dbt Core и dbt Fusion как два поколения движка

У dbt сейчас одновременно живут два поколения исполняющего движка. dbt Core 1.x написан на Python, это стабильная поддерживаемая линейка, именно она стоит на стенде для этой статьи в версии 1.12.3 вместе с адаптером dbt-postgres 1.11.0. Параллельно с июля 2026 года на ветке Fusion Stable доступен dbt Fusion engine, движок нового поколения на Rust, а поверх него собирается dbt Core 2.0, переписанный CLI в статусе Beta после анонса alpha-версии в июне 2026 на Snowflake Summit, ежегодной конференции вендора Snowflake. Документация dbt Labs прямо называет связку Fusion и Core 2.0 текущим поколением инструмента, а линию 1.x относит к legacy, при этом поддержку она продолжает получать.

 

Архитектура dbt Core на Python и dbt Fusion engine на Rust: два поколения исполняющего движка

Модели, адаптеры и DAG зависимостей

Базовая единица работы в dbt это модель, файл с расширением .sql, где обычный SELECT дополнен функциями Jinja. Модели живут внутри dbt-проекта, каталога с файлом dbt_project.yml, который задаёт имя проекта, пути к моделям и материализацию по умолчанию для каждой папки. Подключение к конкретному хранилищу, будь то Snowflake, BigQuery, Databricks, Redshift или Postgres, dbt выносит в отдельный компонент, адаптер, и делит их на доверенные (trusted), которые поддерживают dbt Labs и партнёры, и адаптеры сообщества.

Граф зависимостей моделей (DAG) dbt строит не по порядку файлов и не по ручному списку, а по функции ref() внутри самих моделей. Если модель B ссылается на модель A через {{ ref(‘A’) }}, dbt знает, что A нужно построить раньше B, и включает эту связь в граф автоматически, та же логика работает для сырых источников через функцию source(). Рядом с этим графом у dbt есть dbt Semantic Layer на движке MetricFlow, он превращает измерения и меры в готовые бизнес-метрики, этой теме посвящена отдельная статья про семантический слой.

DAG зависимостей моделей dbt: staging и mart слои связаны через ref()

 

Как dbt работает под капотом от Jinja до материализации

Когда запускается команда dbt build или dbt run, dbt сначала парсит все файлы проекта и прогоняет каждую модель через шаблонизатор Jinja. На этом шаге разворачиваются макросы, а вызовы ref() и source() превращаются в полные имена таблиц конкретного хранилища. Результат, чистый SQL без единой Jinja-конструкции, dbt сохраняет в каталоге compiled и именно его отправляет в базу.

Пока идёт компиляция, dbt параллельно строит граф зависимостей из собранных вызовов ref() и source(), а затем выполняет модели в топологическом порядке графа, а не в порядке файлов в папке. Демо-прогон показывает это напрямую. Модель stg_orders, у которой нет зависимостей внутри проекта, построилась первым шагом, 1 of 8, а fct_daily_orders, которая ссылается на stg_orders через ref(), только пятым, 5 of 8, хотя оба файла лежат в проекте на одном уровне вложенности. На последнем шаге dbt материализует каждую модель по её конфигу, view оборачивает SELECT в объект без физического хранения строк, а table выполняет CREATE TABLE AS и сохраняет результат на диске. Порядок при этом виден только в реальном логе dbt build или dbt run, команда dbt ls модели по графу не сортирует, при подготовке демо она напечатала mart-модель раньше staging, хотя строится та позже.

 

Эволюция dbt Core от Python к dbt Fusion на Rust

По официальному описанию Fusion engine, этот рантайм на Rust под лицензией Apache 2.0 даёт более быструю компиляцию и более богатую среду разработки по сравнению с линейкой v1.

 

Параметр dbt Core 1.x dbt Fusion / dbt Core 2.0
Язык реализации Python Rust
Статус на август 2026 Legacy, поддерживается Fusion engine — GA, dbt Core 2.0 — Beta
Лицензия Apache 2.0 Apache 2.0
Компиляция и среда разработки Точка сравнения в описании Fusion Быстрее и богаче, чем v1

 

Эволюция заметна и внутри самой линии 1.x, без перехода на Rust. Демо-проект собран на dbt-core 1.12.3, и попытка задать аргументы generic-теста по старым примерам из документации, прямо на верхнем уровне теста, а не вложенными, вызывает предупреждение MissingArgumentsPropertyInGenericTestDeprecation при dbt parse и dbt build. Тест из-за этого не падает, но 1.12.x уже требует вкладывать аргументы accepted_values.values и relationships.to и field под отдельный ключ arguments, и файл schema.yml в этой статье написан именно так. Такое предупреждение сегодня и жёсткая ошибка завтра, обычный сценарий взросления инструмента, который живёт в проде у тысяч команд.

 

dbt в Modern Data Stack между хранилищем и оркестрацией

В типовом Modern Data Stack данные сначала попадают в хранилище инструментом извлечения и загрузки вроде Fivetran или Airbyte, без трансформации, ровно так, как они выглядят в источнике. dbt берётся за работу уже после этого шага и выполняет всю трансформацию средствами самого хранилища, компилируя модели в SQL и отправляя их движку DWH. Такой порядок называется ELT в отличие от классического ETL, где трансформация происходила во внешнем движке ещё до загрузки, и именно за букву T в этой аббревиатуре отвечает dbt.

У dbt Core нет собственного планировщика, он не решает сам, когда именно ему запуститься. За это отвечает оркестрация, чаще всего Apache Airflow, который вызывает dbt build как один из шагов в более широком DAG пайплайна, например после проверки, что вчерашняя загрузка данных в источник завершилась. Вместе с моделями dbt бесплатно даёт происхождение данных (data lineage). Команда dbt docs generate строит сайт документации с графом связей между моделями, восстановленным из тех же вызовов ref(), по которому видно, из каких таблиц собрана любая витрина.

Архитектура Данных

Код курса
ARMG
Ближайшая дата курса
28 сентября, 2026
Продолжительность
24 ак.часов
Стоимость обучения
76 800

 

Сценарии использования и отличительные черты

dbt выигрывает там, где трансформацию данных пишет не один программист, а команда, в которой есть аналитики, знающие SQL, но не обязательно Python. Он даёт этой команде общий язык и общую дисциплину разработки.

  • Версионирование через git. Модель это текстовый файл, изменения проходят через pull request и код-ревью, как в любом программном проекте.
  • Тесты как часть проекта. Проверки not_null, unique, accepted_values и relationships пишутся декларативно в YAML и запускаются вместе с моделями, а не отдельным скриптом, который легко забыть прогнать.
  • Переиспользование SQL через Jinja-макросы. Повторяющийся кусок логики выносится в макрос один раз и подключается в любой модели вместо копирования запроса по десяти файлам.
  • Вычисления внутри хранилища. dbt не поднимает отдельный кластер, он компилирует SQL и отдаёт его тому же движку, где уже лежат данные, поэтому масштабируется ровно так, как масштабируется само хранилище.

Обратная сторона этих же черт задаёт границу применимости. Если данные нужно обогатить внешним API, распарсить неструктурированный файл или прогнать через модель машинного обучения до того, как они лягут в витрину, SQL-моделей dbt для этого недостаточно, и такая логика остаётся в отдельном ETL-шаге до dbt или после него.

Apache Airflow для инженеров данных

Код курса
AIRF
Ближайшая дата курса
21 сентября, 2026
Продолжительность
24 ак.часов
Стоимость обучения
76 800

 

Практика. DAG моделей, материализации и тесты на демо-данных

dbt (data build tool) from airflow import DAG from airflow.operators.bash import BashOperator from datetime import datetime with DAG( dag_id="spark_submit_demo", start_date=datetime(2025, 1, 1), schedule="@daily", catchup=False ) as dag: run = BashOperator( task_id="run_job", bash_command="spark-submit app.py" ) GitHub code example dbt (data build tool)

Демо переиспользует таблицу orders из базы semantic_layer_demo на локальном Postgres 18.4, две тысячи строк заказов интернет-магазина, тот же источник, что и в статье про семантический слой. Полный код лежит в репозитории на GitHub, коммит e46105d, курс «Data Build Tool для инженеров данных» разбирает те же темы гораздо подробнее, а более крупный производственный кейс, dbt поверх Apache Spark в AWS, разобран в статье блога «Как упростить работу с DWH и Data Lake: DBT + Apache Spark в AWS».

dbt_project.yml задаёт два слоя моделей с разной материализацией по умолчанию, staging как view, marts как table.

# dbt-core 1.12.3, прогнано на стенде 2026-08-28
name: 'dbt_demo'
version: '1.0.0'
config-version: 2

profile: 'dbt_demo'

model-paths: ["models"]
clean-targets: ["target", "dbt_packages"]

models:
  dbt_demo:
    staging:
      +materialized: view
    marts:
      +materialized: table

Модель staging приводит сырую таблицу orders к чистому виду один в один по строкам, без агрегации, поэтому её материализация и остаётся view, ей незачем занимать место на диске.

-- dbt-core 1.12.3, синтаксис Jinja по документации dbt Core, прогнано на стенде 2026-08-28
-- Слой staging: приводит сырую таблицу orders к чистому виду один в один по строкам,
-- без агрегации. Материализация view (см. dbt_project.yml) - слой staging не хранит
-- собственных данных, только переиспользуемый SQL поверх источника.
select
    order_id,
    customer_id,
    order_date,
    region,
    lower(status) as status,
    amount
from {{ source('raw', 'orders') }}

Модель mart агрегирует staging до дневных показателей по региону и ссылается на stg_orders через ref(), а не по имени таблицы текстом, именно эта ссылка и строит связь в DAG.

-- dbt-core 1.12.3, синтаксис Jinja по документации dbt Core, прогнано на стенде 2026-08-28
-- Слой mart: агрегирует staging-модель до дневных показателей по региону.
-- ref() строит зависимость stg_orders -> fct_daily_orders в DAG dbt, а не текстом
-- имени таблицы - dbt сам определяет порядок построения моделей по графу ref().
-- Материализация table (см. dbt_project.yml): агрегат физически пересчитывается
-- и сохраняется, а не оборачивается в view поверх staging при каждом обращении.
select
    order_date,
    region,
    count(*) as orders_count,
    sum(amount) filter (where status = 'completed') as completed_amount,
    sum(amount) as total_amount
from {{ ref('stg_orders') }}
group by order_date, region

Шесть тестов проверяют обе модели четырьмя встроенными generic-тестами dbt Core, not_null, unique, accepted_values и relationships, аргументы accepted_values и relationships вложены под ключ arguments по требованию 1.12.x.

# dbt-core 1.12.3, вложение arguments по deprecation-предупреждению стенда, прогнано на стенде 2026-08-28
version: 2

models:
  - name: stg_orders
    columns:
      - name: order_id
        data_tests:
          - not_null
          - unique
      - name: status
        data_tests:
          - accepted_values:
              arguments:
                values: ['completed', 'pending', 'cancelled', 'refunded']

  - name: fct_daily_orders
    columns:
      - name: order_date
        data_tests:
          - not_null
      - name: region
        data_tests:
          - not_null
          - relationships:
              arguments:
                to: ref('stg_orders')
                field: region

Публичный запуск для этой статьи выполняется одной командой из RUNBOOK.md репозитория.

# dbt-core 1.12.3, прогнано на стенде 2026-08-28
DBT_PROFILES_DIR=. dbt build

Восемь шагов подряд, две модели и шесть тестов, все со статусом PASS, а порядок 1 of 8 раньше 5 of 8 это прямая иллюстрация DAG из предыдущего раздела.

# вывод run_output.txt, стенд 2026-08-28
1 of 8 START sql view model public.stg_orders .................................. [RUN]
1 of 8 OK created sql view model public.stg_orders ............................. [CREATE VIEW in 0.07s]
5 of 8 START sql table model public.fct_daily_orders ........................... [RUN]
5 of 8 OK created sql table model public.fct_daily_orders ...................... [SELECT 1349 in 0.05s]
8 of 8 PASS relationships_fct_daily_orders_region__region__ref_stg_orders_ ..... [PASS in 0.03s]
Done. PASS=8 WARN=0 ERROR=0 SKIP=0 NO-OP=0 REUSED=0 TOTAL=8

Полный прогон на сервере занял 0.33 секунды на 1349 строк агрегата, а с учётом старта самого CLI процесс укладывается примерно в 3 секунды. В fct_daily_orders значение completed_amount иногда пустое, NULL, для пары дата-регион без заказов со статусом completed. Это ожидаемое поведение агрегатной функции sum(…) filter (…), а не ошибка в модели или в тестах.

 

Заключение

dbt не заменяет ни хранилище данных, ни оркестратор, а закрывает узкий, но самый часто меняющийся участок пайплайна, SQL-трансформацию сырых таблиц в аналитические витрины. Взамен на отказ от отдельного вычислительного кластера он даёт версионируемый, тестируемый и документированный граф моделей, который сам вычисляет порядок построения по ref(), а не по памяти команды. Переход экосистемы на Rust через dbt Fusion меняет скорость компиляции, но архитектурная роль инструмента, буква T между хранилищем и оркестрацией, остаётся той же, что и в первых версиях на Python.

 

Референсные ссылки