Перейти к основному содержанию
Материализация materialized_view должна представлять собой SELECT из существующей исходной таблицы. В отличие от PostgreSQL, materialized view в ClickHouse не является «статическим» объектом (и для неё не существует соответствующей операции REFRESH). Вместо этого она работает как триггер вставки: новые строки вставляются в целевую таблицу за счёт применения заданного преобразования SELECT к строкам, вставляемым в исходную таблицу. Подробнее о том, как работают materialized view в ClickHouse, см. в документации ClickHouse по materialized view.
Общие сведения о концепциях материализации и общих конфигурациях (engine, order_by, partition_by и т. д.) см. на странице Materializations.

Как управляется целевая таблица

Когда вы используете материализацию materialized_view, dbt-clickhouse должен создать и materialized view, и целевую таблицу, в которую вставляются преобразованные строки. Управлять целевой таблицей можно двумя способами: Выбранный подход влияет на то, как обрабатываются изменения схемы, полные обновления и конфигурации с несколькими materialized view. В следующих разделах каждый подход описан подробно.

Материализация с неявной целевой таблицей

Это поведение по умолчанию. Когда вы определяете модель materialized_view, адаптер:
  1. Создаёт целевую таблицу с именем модели
  2. Создаёт в ClickHouse materialized view с именем <model_name>_mv
Схема целевой таблицы выводится на основе столбцов в операторе SELECT materialized view. Все ресурсы (целевая таблица + materialized view) используют одну и ту же конфигурацию модели.
См. тестовый файл с дополнительными примерами.
Вы также можете задать для столбцов целевой таблицы кодек и TTL, если принудительно включите контракт модели. Подробнее см. в разделе Конфигурация столбцов.

Несколько materialized view

ClickHouse позволяет нескольким materialized view записывать строки в одну и ту же целевую таблицу. Чтобы поддержать это в dbt-clickhouse при использовании подхода с неявной целевой таблицей, вы можете создать UNION в файле модели, заключив SQL для каждого materialized view между комментариями вида --my_mv_name:begin и --my_mv_name:end. Например, следующий код создаст два materialized view, которые будут записывать данные в одну и ту же целевую таблицу модели. Имена materialized view будут иметь вид <model_name>_mv1 и <model_name>_mv2:
При обновлении model с несколькими materialized views (MV), особенно если вы переименовываете один из MV, dbt-clickhouse не удаляет старый MV автоматически. Вместо этого вы увидите следующее предупреждение:Warning - Table <previous table name> was detected with the same pattern as model name <your model name> but was not found in this run. In case it is a renamed mv that was previously part of this model, drop it manually (!!!)

Как изменять схему целевой таблицы

Начиная с dbt-clickhouse версии 1.9.8, вы можете управлять тем, как изменяется схема целевой таблицы, когда dbt run обнаруживает различия в столбцах в SQL-коде materialized view.
По умолчанию dbt не применяет никаких изменений к целевой таблице (значение настройки — ignore), но вы можете изменить эту настройку, чтобы она вела себя так же, как config on_schema_change в incremental-моделях. Кроме того, эту настройку можно использовать как механизм защиты. Если задать для неё значение fail, сборка завершится ошибкой, если столбцы в SQL materialized view отличаются от столбцов в целевой таблице, созданной при первом dbt run.

Догрузка данных

По умолчанию при создании или повторном создании materialized view (MV) целевая таблица сначала заполняется историческими данными, и только после этого создаётся сама materialized view (catchup=True). Это поведение можно отключить, установив параметру catchup значение False.
Риск потери данных при полном обновленииИспользование catchup: False с dbt run --full-refresh приведет к удалению всех существующих данных в целевой таблице. Таблица будет пересоздана пустой и в дальнейшем будет обрабатывать только новые данные. Убедитесь, что у вас есть резервные копии, если исторические данные могут понадобиться позже.

Материализация с явной целевой таблицей (бета)

БетаЭта возможность находится в статусе бета и доступна, начиная с версии dbt-clickhouse 1.10. API может измениться на основе отзывов сообщества.
По умолчанию dbt-clickhouse создает и управляет и целевой таблицей, и materialized view в рамках одной модели (подход с неявной целевой таблицей, описанный выше). У этого подхода есть ряд ограничений:
  • Все ресурсы (целевая таблица + MV) используют одну и ту же конфигурацию. Если несколько MV указывают на одну и ту же целевую таблицу, их нужно определять вместе с помощью синтаксиса UNION ALL.
  • Этими ресурсами нельзя управлять по отдельности — все они должны описываться в одном и том же файле модели.
  • Нельзя легко задать имя для каждой MV.
  • Все настройки общие для целевой таблицы и MV, поэтому сложно настраивать каждый ресурс отдельно и понимать, какая конфигурация к какому ресурсу относится.
Возможность явной целевой таблицы позволяет определить целевую таблицу отдельно как обычную материализацию table, а затем ссылаться на нее из моделей materialized view.

Преимущества

  • Полностью разделённые ресурсы: Теперь каждый ресурс можно определять отдельно, что улучшает читаемость
  • Соответствие ресурсов 1:1 между dbt и CH: Теперь вы можете использовать инструменты dbt, чтобы управлять ими и изменять их по отдельности.
  • Теперь доступны разные конфигурации: Теперь к каждому из них можно применять отдельную конфигурацию.
  • Больше не нужно придерживаться соглашений об именовании: Теперь все ресурсы создаются с использованием заданного вами имени, а не пользовательского имени с добавлением _mv для MV.

Ограничения

  • Определение целевой таблицы не вполне естественно для dbt: это не SQL-запрос, который читает из исходной таблицы, поэтому здесь вы лишаетесь проверок dbt. SQL materialized view по-прежнему будет проверяться с помощью утилит dbt, а его совместимость со столбцами целевой таблицы — на уровне CH.
  • Мы обнаружили несколько проблем, связанных с ограничениями функции ref(): она нужна, чтобы ссылаться на модели друг из друга, но её можно использовать только для ссылок на вышестоящие модели, а не на нижестоящие. Это создаёт определённые сложности для данной реализации. Мы создали issue в репозитории dbt-core и сейчас обсуждаем с их командой возможные решения (dbt-labs/dbt-core#12319):
    • Когда ref() вызывается внутри блока config, она возвращает текущую модель, а не ту, на которую должна ссылаться. Из-за этого мы не можем определить её в секции config() и вынуждены использовать комментарий, чтобы добавить эту зависимость. Мы придерживаемся того же подхода, что описан в документации dbt: вариант с “—depends_on:”.
    • ref() нам подходит, поскольку заставляет сначала создать целевую таблицу, но на графе зависимостей в сгенерированной документации целевая таблица будет показана как ещё одна вышестоящая зависимость, а не как нижестоящая, из-за чего это немного сложнее понять.
    • unit-test также вынуждает нас задавать данные для целевой таблицы, даже если по задумке чтения из неё быть не должно. Обходной путь здесь простой — оставить данные для этой таблицы пустыми.

Использование

Шаг 1: Определите целевую таблицу как обычную табличную модель Модель events_daily.sql:
Это обходной путь, о котором мы упоминаем в разделе ограничений. Здесь вы можете потерять часть проверок dbt, но схема по-прежнему будет проверяться на уровне ClickHouse. Шаг 2: Определите materialized view, указывающие на целевую таблицу Например, можно определить разные MV в разных моделях, в том числе направив их в одну и ту же целевую таблицу. Обратите внимание на новый вызов макроса {{ materialization_target_table(ref('events_daily')) }}, который задаёт целевую таблицу для MV. Модель page_events_aggregator.sql:
Модель mobile_events_aggregator.sql:

Параметры конфигурации

При использовании явных целевых таблиц, помимо общих конфигураций материализации и конфигураций для таблиц, применяются следующие конфигурации: Для целевой таблицы (materialized='table'): Для materialized view (materialized='materialized_view'):
Обычно достаточно установить catchup в True для MV либо repopulate_from_mvs_on_full_refresh в True для их целевых таблиц. Если установить True для обоих параметров, это может привести к дублированию данных.

Основные операции

Полное обновление с явными целевыми таблицами

При использовании --full-refresh явные целевые таблицы будут пересозданы (поэтому вы можете потерять данные, если в ходе этого процесса продолжается ингестия). Поведение будет различаться в зависимости от конфигурации: Вариант 1: поведение --full-refresh по умолчанию. Всё будет пересоздано, но во время пересоздания MV целевая таблица будет пустой или загруженной лишь частично. Всё будет удалено и создано заново. Если вы хотите повторно выполнить вставку данных с помощью SQL для MV, оставьте параметр catchup=True:
Вариант 2: Я хочу заново создать целевую таблицу и не хочу, чтобы во время пересоздания MV читались пустые данные. Если вам сначала нужно обновить SQL у MV, вы можете задать в них catchup=False, а затем выполнить dbt run или dbt run --full-refresh для MV. Убедитесь, что MV созданы до запуска --full-refresh для целевой таблицы, так как при этом используются определения MV из ClickHouse. Установите repopulate_from_mvs_on_full_refresh=True в модели целевой таблицы. При выполнении dbt run --full-refresh произойдет следующее:
  1. Будет создана новая временная таблица
  2. Будет выполнен INSERT-SELECT с использованием SQL каждой MV
  3. Таблицы будут атомарно обменены местами
Таким образом, в вашей таблице не будет пустых данных, пока MV пересоздаются.

Изменение целевой таблицы

Вы не можете изменить целевую таблицу MV без --full-refresh. Если после изменения ссылки materialization_target_table() попробовать выполнить обычную команду dbt run, сборка завершится ошибкой с сообщением о том, что целевая таблица была изменена. Чтобы изменить целевую таблицу:
  1. Обновите вызов materialization_target_table()
  2. Выполните dbt run --full-refresh -s your_mv_model

Устранение типичных неполадок

Целевая таблица пуста во время или после выполнения run

Это может происходить по нескольким причинам:
  • Для materialized view может быть задано catchup=False, или для целевой таблицы может быть задано repopulate_from_mvs_on_full_refresh=False, поэтому при создании materialized views или пересоздании целевой таблицы дозагрузка не выполняется. Это ожидаемое поведение, поэтому, если вы хотите повторно вставить данные с помощью SQL materialized views, убедитесь, что для materialized view установлено catchup=True (это значение по умолчанию) или что для целевой таблицы задано repopulate_from_mvs_on_full_refresh=True. Не включайте оба параметра одновременно, чтобы избежать дубликатов. Подробнее см. в разделе конфигурации.
  • Во время выполнения dbt run --full-refresh, если materialized views используют значение по умолчанию catchup=True, целевая таблица будет пересоздана, а MV последовательно повторно вставят данные. Чтобы избежать этой ситуации, см. раздел Полное обновление с явными целевыми таблицами.

dbt run --full-refresh для целевой таблицы с repopulate_from_mvs_on_full_refresh=True использует логику из старых версий materialized view, а не из SQL, который сейчас находится в проекте

repopulate_from_mvs_on_full_refresh=True использует существующий SQL для MV, уже определённый в ClickHouse. Чтобы использовалось новое определение materialized view, выполните dbt run для каждой materialized view перед запуском dbt run --full-refresh для целевой таблицы.

После выполнения запуска появляются дубликаты данных

Возможные причины:
  • Одновременно могут быть включены и catchup=True для materialized view, и repopulate_from_mvs_on_full_refresh=True для целевой таблицы: оставьте только один из этих параметров в зависимости от того, какие операции вы хотите выполнять. Подробнее см. в разделе конфигурации.
  • Целевая таблица определена без WHERE 0: целевая таблица должна создаваться пустой, но внутренний запрос может вставить данные, если не указано WHERE 0. Убедитесь, что это условие добавлено.

Потеря данных во время активной ингестии после выполнения dbt run --full-refresh

Некоторые строки из исходной таблицы отсутствуют в целевой таблице после выполнения dbt run --full-refresh. materialized view в ClickHouse работают как триггеры вставки — они захватывают данные только пока существуют. Во время полного обновления возникает короткое окно, когда MV удаляется и создаётся заново («слепое окно»). Любые строки, вставленные в исходную таблицу в течение этого окна, не будут захвачены. Подробнее см. в разделе Поведение во время активной ингестии.

Методы отладки

Проверьте текущую целевую таблицу MV в ClickHouse

Выполните запрос к system.tables, чтобы посмотреть, в какую таблицу пишет materialized view:

Проверьте, распознаёт ли dbt таблицу как целевую таблицу materialized view

Во время запуска dbt найдите в журнале следующее сообщение:
Таблица <table_name> используется как целевая таблица для materialized view, управляемого dbt. Чтобы предотвратить потерю данных, для mv_on_schema_change по умолчанию устанавливается значение “fail”.
Если это сообщение появляется, значит dbt обнаружил, что таблица служит целевой как минимум для одного materialized view, управляемого dbt. Если вы ожидаете увидеть это сообщение, но его нет, проверьте следующее:
  • Модель materialized view корректно задаёт {{ materialization_target_table(ref('your_target')) }}
  • В config модели materialized view задано materialized='materialized_view'
  • И materialized view, и целевая таблица были выполнены как минимум один раз

Переход от неявной к явной целевой таблице

Если у вас уже есть модели materialized view, использующие подход с неявной целевой таблицей, и вы хотите перейти на подход с явной целевой таблицей, выполните следующие шаги: 1. Создайте модель целевой таблицы Создайте новый файл модели с materialized='table', который определяет ту же схему, что и текущая целевая таблица MV. Используйте условие WHERE 0, чтобы создать пустую таблицу. Используйте то же имя, что и у текущей неявной модели materialized view. После этого вы сможете использовать эту модель для дальнейшей доработки целевой таблицы.
2. Обновите модели MV Создайте новые модели, каждая из которых должна включать SQL-код MV и вызов macro materialization_target_table(), указывающий на новую целевую таблицу. Если ранее вы использовали UNION ALL, удалите эту часть и комментарии. Для имён моделей нужно придерживаться следующего соглашения об именовании:
  • если была определена только одна MV, она должна называться: <old_model_name>_mv
  • если было определено несколько MV, каждая должна называться: <old_model_name>_mv_<name_in_comments>
Ранее в my_model.sql (неявная целевая таблица, одна модель с UNION ALL):
После (явная целевая таблица, отдельные файлы моделей):
3. При необходимости повторяйте эти действия, следуя инструкциям в разделе явная целевая таблица.

Сравнение поведения подходов с неявной и явной целевой таблицей

Общее поведение

Поведение во время активной ингестии

При итерации над моделями важно учитывать, как разные операции взаимодействуют с вставляемыми данными:
  • Поскольку materialized view в ClickHouse работают как триггеры вставки, они захватывают данные только пока существуют. Если materialized view удаляется и создаётся заново (например, во время --full-refresh), любые строки, вставленные в исходную таблицу в этот промежуток, не будут обработаны materialized view. Это состояние называют «слепотой» materialized view.
  • Все процессы catchup основаны на операциях INSERT INTO ... SELECT, использующих SQL materialized view, и не зависят от того, как работают сами materialized view. После запуска INSERT новые данные в него уже не попадают, но будут захвачены присоединённой materialized view.
В следующей таблице приведено, насколько безопасна каждая операция, когда в исходную таблицу активно выполняются вставки.

Операции с неявной целевой таблицей

Операции с явной целевой таблицей

Модели materialized view: Модель целевой таблицы:
Рекомендации для продакшн-окружений с активной ингестией
  • По возможности приостанавливайте ингестию на время операций dbt: так все операции будут безопасными, и данные не потеряются.
  • По возможности используйте движок с дедупликацией (например, ReplacingMergeTree) для целевой таблицы, чтобы обрабатывать возможные дубликаты из-за пересечений при дозагрузке.
  • По возможности предпочитайте ALTER TABLE ... MODIFY QUERY (обычный dbt run без --full-refresh) — это всегда безопасно.
  • Учитывайте проблемные окна во время операций dbt.

Refreshable Materialized Views

Refreshable Materialized Views — это особый тип materialized view в ClickHouse, который периодически заново выполняет запрос и сохраняет результат — аналогично materialized view в других базах данных. Это полезно в сценариях, где нужны периодические снимки или агрегации, а не триггеры вставки в реальном времени.
Refreshable materialized view можно использовать как с подходом неявная целевая таблица, так и с подходом явная целевая таблица. Конфигурация refreshable не зависит от того, как управляется целевая таблица.
Чтобы использовать refreshable materialized view, добавьте объект конфигурации refreshable в модель MV со следующими параметрами:

Пример с неявной целевой таблицей

Пример с явной целевой таблицей

Ограничения

  • При создании в ClickHouse refreshable materialized view (MV) с зависимостью ClickHouse не генерирует ошибку, если указанная зависимость не существует на момент создания. Вместо этого refreshable MV остается в неактивном состоянии и ожидает, пока зависимость не будет удовлетворена, прежде чем начнет обрабатывать обновления или обновляться. Такое поведение является штатным, но оно может приводить к задержкам в доступности данных, если требуемая зависимость не будет своевременно создана. Перед созданием refreshable materialized view следует убедиться, что все зависимости корректно определены и существуют.
  • На данный момент фактическая «связь dbt» между mv и его зависимостями отсутствует, поэтому порядок создания не гарантируется.
  • Возможность refreshable не тестировалась для нескольких mv, направляющих данные в одну и ту же целевую модель.
Последнее изменение 25 июня 2026 г.