Recap is_incremental() + философия incremental
Этот модуль — самый сложный в курсе. Incremental models на dbt I вы видели на базовом уровне: «если данные большие, ставим materialized=‘incremental’». На production-масштабе всё сложнее: четыре разных стратегии, тонкие гарантии каждой, специфические gotchas, performance-trade-offs на миллиардах строк. Этот урок — короткий recap основ и философия, на которой строятся следующие четыре урока модуля.
is_incremental(), unique_key, on_schema_change, 4 условия incremental — это материал dbt-i/06. Если помнишь — переходи к разделу “Философия incremental: когда нужен и когда нет”. Если хочешь освежить — сюда.
Recap в одной таблице
| Концепт | Что значит | Стратегии |
|---|---|---|
is_incremental() | True если: модель materialized=incremental, таблица существует, run не —full-refresh, не unit test | все |
unique_key | колонка(ы) уникальной строки | merge/delete+insert обязателен; append/microbatch — нет |
--full-refresh | DROP + CREATE, как первый run | блокирует is_incremental() |
on_schema_change | ignore (default), append_new_columns, sync_all_columns, fail | критичен на проде |
| 4 стратегии 1.10 | append, delete+insert, merge, microbatch (+ insert_overwrite для BQ/Spark) | по объёму и dedup |
Это базовый минимум — детали и edge cases следующих уроков предполагают эти знания.
Философия incremental: когда нужен и когда нет
dbt-i: базовые incremental — is_incremental() и unique_key ACID: что гарантирует транзакция под incrementalIncremental не «всегда лучше table». У него есть стоимость:
Incremental экономит compute на большом объёме, но добавляет сложность. На малых объёмах overhead не окупается.
Правила, когда нужен incremental:
- Таблица большая — 10M+ строк, табличный run занимает заметное время (минуты+).
- Дельта маленькая — на каждый run новых строк меньше 10% существующих.
- Есть watermark — колонка типа
created_at, по которой можно отличать новое от старого. - Хочется частых runs — каждые 15 минут или чаще.
Правила, когда НЕ нужен incremental:
- Таблица маленькая — менее 1M строк, table run меньше 10 секунд. Overhead incremental не окупится.
- Дельта большая — на каждый run меняется 30%+ строк. Incremental тут медленнее, чем table.
- Нет watermark — нельзя надёжно определить, что новое. Можно делать full сравнение, но это та же table.
- Backfill частый — если каждый месяц меняется логика, и нужно пересчитать всё, table проще.
Production-цифры
Конкретные числа из реальных проектов 2024-2026:
- Маленькая таблица (1M строк): table run на DuckDB локально — 1-2 секунды. Incremental — те же 1-2 секунды (overhead на проверку, что таблица существует). Выигрыш нулевой.
- Средняя (50M строк): table run — 30-60 секунд. Incremental (1M дельты) — 2-5 секунд. Выигрыш 10-30x.
- Большая (1B+ строк): table run — 10-30 минут на Snowflake medium warehouse, 0.10-0.30 за run. Выигрыш 50-100x по compute, 30-50x по cost.
- Огромная (10B+ строк): table run — часы, десятки долларов. Incremental — 2-5 минут, $0.50-2. Выигрыш 100x+.
На production-цифрах incremental — единственный способ работать с большими таблицами в разумные сроки и бюджеты.
Стратегии — что покрывает этот модуль
Этот модуль (02) разберёт append, delete+insert, merge. Microbatch — отдельный модуль (03). insert_overwrite не покрываем подробно (warehouse-specific и редкий случай).
DuckDB-specific
На DuckDB поддержаны:
append— работает.delete+insert— работает.merge— DuckDB 1.4+. На 1.3 и старее — config error. Если у вас DuckDB 1.3 (или dbt-duckdb старее 1.10.0), merge не доступен.microbatch— dbt-core 1.9+. Работает без unique_key (по дизайну, не баг).
В production на Snowflake/BigQuery все стратегии работают без version-ограничений.
Жизненный цикл incremental-модели
Три состояния incremental-модели в production. Каждое имеет свою логику и свои риски.
Гарантии и риски incremental
Главные риски incremental в production:
- Дубли — append без unique_key проинсертит ту же строку дважды, если её источник прислал повторно.
- Пропущенные строки — фильтр
created_at > max(created_at)пропустит строку с late-arriving timestamp (событие случилось вчера, но пришло сегодня — фильтр отсечёт). - Дрейф схемы — добавление колонки в source может тихо игнорироваться incremental-моделью (есть
on_schema_changeпараметр для управления). - Race conditions — два dbt run одновременно могут вставить одни и те же строки дважды.
- Forgotten full-refresh — изменили бизнес-логику в модели, забыли запустить full-refresh — историческая часть таблицы осталась со старой логикой.
Каждый из этих рисков мы разберём в этом и следующем модулях с конкретными решениями.
Когда incremental — это плохо
Иногда команды переусердствуют с incremental:
- «Везде incremental, потому что быстрее» — на 100 маленьких моделях incremental добавляет сложность без выигрыша. Профайлинг покажет, что table вообще норм.
- «Incremental на dimension-таблице» — dim_customers обычно маленькая, и обычно полностью пересчитывается (изменилось всё, что угодно). Incremental тут — overhead.
- «Incremental без unique_key на не-append логике» — гарантированные дубли, и в первый production-инцидент это заметит downstream.
Правило: по умолчанию table или view, incremental только когда профайлинг показал, что table не выдерживает. Это контр-интуитивно для junior, но это правильная философия для middle.
Framework выбора стратегии
Idempotency в Airflow-DAGs — параллельная проблемаПосле того как профайлинг подтвердил «table не выдерживает», следующий вопрос — какую из четырёх стратегий выбрать. Decision tree:
Алгоритм принятия решения на основе характеристик данных и downstream-требований.
В реальных проектах распределение примерно: 40% merge, 30% append (логи и события), 20% delete+insert (legacy Postgres), 10% microbatch (heavy backfills). Если у вас перекос в сторону одной стратегии — это сигнал либо технологического outlier (вся компания на Postgres), либо что команда не пробовала альтернативы.
Что дальше в этом модуле
- 02-append.mdx — append стратегия, immutable logs use case, no-dedup workflow.
- 03-delete-insert-merge.mdx — delete+insert vs merge, deep dive, когда какая.
- 04-incremental-predicates.mdx — incremental_predicates для оптимизации MERGE, DBT_INTERNAL_DEST trick.
- 05-lookback-and-late-arriving.mdx — паттерн lookback, late-arriving data.
После этого модуля вы будете уметь выбирать правильную стратегию для конкретной задачи и понимать trade-offs.
Попробуй сам
В своём проекте проверьте:
- Сколько у вас incremental-моделей? (Скрипт:
find models -name "*.sql" -exec grep -l "materialized='incremental'" {} \;) - Для каждой запишите: размер таблицы (rows), время table-run, время incremental-run. Если разница меньше 5x, возможно incremental не нужна.
- Найдите incremental-модели без
unique_key. Если стратегия не append — это потенциальный баг с дублями. - Запустите
dbt run --full-refreshдля одной из incremental-моделей. Засеките время. Если это > 30 минут — у вас будет проблема при backfill.