Перейти к основному содержанию
В этом разделе приведены руководства по настройке dbt и адаптера ClickHouse, а также пример использования dbt с ClickHouse на общедоступном наборе данных IMDB. Пример включает следующие шаги:
  1. Создание проекта dbt и настройка адаптера ClickHouse.
  2. Определение модели.
  3. Обновление модели.
  4. Создание инкрементальной модели.
  5. Создание модели-снимка.
  6. Использование materialized views.
Эти руководства предназначены для использования вместе с остальной документацией, разделом возможностей и конфигураций и справочником по материализациям.

Настройка

Следуйте инструкциям в разделе Настройка dbt и адаптера ClickHouse, чтобы подготовить окружение. Важно: приведённые ниже инструкции протестированы с Python 3.9.

Подготовьте ClickHouse

dbt особенно хорошо подходит для моделирования сильно связанных реляционных данных. В качестве примера мы используем небольшой набор данных IMDB со следующей реляционной схемой. Этот набор данных взят из репозитория реляционных наборов данных. По сравнению с типичными схемами, используемыми в dbt, он весьма прост, но при этом представляет собой удобный небольшой образец: Как показано ниже, мы используем подмножество этих таблиц. Создайте следующие таблицы:
Столбец created_at в таблице roles; по умолчанию для него задано значение now(). Позже мы используем его, чтобы определять инкрементальные обновления наших моделей — см. Инкрементальные модели.
Мы используем функцию s3, чтобы читать исходные данные из общедоступных конечных точек и выполнять вставку данных. Выполните следующие команды, чтобы заполнить таблицы:
Время выполнения этих действий может различаться в зависимости от пропускной способности вашего соединения, но каждое из них должно занимать всего несколько секунд. Выполните следующий запрос, чтобы получить сводку по каждому актёру, отсортированную по количеству появлений в фильмах, и убедиться, что данные были успешно загружены:
Ответ должен выглядеть следующим образом:
В последующих руководствах мы преобразуем этот запрос в модель и материализуем её в ClickHouse как dbt-представление и таблицу.

Подключение к ClickHouse

  1. Создайте проект dbt. В этом случае мы назовём его по имени нашего источника imdb. Когда появится запрос, выберите clickhouse в качестве источника базы данных.
  2. Перейдите в каталог проекта с помощью cd:
  3. На этом этапе вам понадобится любой текстовый редактор. В примерах ниже мы используем популярный VS Code. Открыв каталог IMDB, вы должны увидеть набор файлов yml и sql:
  4. Обновите файл dbt_project.yml, чтобы указать нашу первую модель — actor_summary, и задайте профиль clickhouse_imdb.
  5. Далее нужно указать для dbt сведения о подключении к вашему экземпляру ClickHouse. Добавьте следующее в ~/.dbt/profiles.yml.
    Обратите внимание: нужно изменить имя пользователя и пароль. Дополнительные доступные настройки описаны здесь.
  6. Находясь в каталоге IMDB, выполните команду dbt debug, чтобы проверить, может ли dbt подключиться к ClickHouse.
    Убедитесь, что в выводе есть строка Connection test: [OK connection ok], которая указывает на успешное подключение.

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

При использовании материализации представления модель при каждом запуске заново создаётся как представление с помощью оператора CREATE VIEW AS в ClickHouse. Это не требует дополнительного хранения данных, но запросы к такому представлению будут выполняться медленнее, чем при материализации в таблицы.
  1. В папке imdb удалите каталог models/example:
  2. Создайте новый файл в каталоге actors внутри папки models. Здесь мы создаем файлы, каждый из которых соответствует отдельной модели actor:
  3. Создайте файлы schema.yml и actor_summary.sql в папке models/actors.
    Файл schema.yml определяет наши таблицы. После этого их можно будет использовать в макросах. Отредактируйте models/actors/schema.yml, чтобы он содержал следующее содержимое:
    actors_summary.sql определяет нашу фактическую модель. Обратите внимание, что в функции config мы также указываем, что модель должна быть материализована как представление в ClickHouse. На наши таблицы есть ссылки из файла schema.yml через функцию source, например source('imdb', 'movies') ссылается на таблицу movies в базе данных imdb. Отредактируйте models/actors/actors_summary.sql, чтобы он содержал следующее:
    Обратите внимание, что мы включаем столбец updated_at в итоговое actor_summary. Позже он понадобится для инкрементальных материализаций.
  4. В каталоге imdb выполните команду dbt run.
  5. dbt представит модель как представление в ClickHouse, как и было запрошено. Теперь мы можем выполнять запросы к этому представлению напрямую. Это представление будет создано в базе данных imdb_dbt — это определяется параметром schema в файле ~/.dbt/profiles.yml в профиле clickhouse_imdb.
    Выполнив запрос к этому представлению, мы можем получить те же результаты, что и в предыдущем запросе, но с более простым синтаксисом:

Создание материализации в таблицу

В предыдущем примере наша модель была материализована как представление. Хотя для некоторых запросов этого может быть достаточно, более сложные SELECT-запросы или часто выполняемые запросы лучше материализовать в таблицу. Такая материализация полезна для моделей, к которым обращаются BI-инструменты, чтобы обеспечить пользователям более высокую скорость работы. По сути, результаты запроса сохраняются в новой таблице с соответствующими накладными расходами на хранение — фактически выполняется INSERT TO SELECT. Обратите внимание, что эта таблица будет пересоздаваться каждый раз, то есть она не является инкрементальной. Поэтому большие результирующие наборы могут приводить к длительному времени выполнения — см. Ограничения dbt.
  1. Измените файл actors_summary.sql, чтобы параметр materialized был установлен в table. Обратите внимание, как задан ORDER BY, а также на то, что мы используем движок таблицы MergeTree:
  2. Из каталога imdb выполните команду dbt run. Выполнение может занять немного больше времени — около 10 с на большинстве машин.
  3. Подтвердите создание таблицы imdb_dbt.actor_summary:
    Вы должны увидеть таблицу с соответствующими типами данных:
  4. Убедитесь, что результаты из этой таблицы совпадают с предыдущими результатами. Обратите внимание на заметное улучшение времени отклика теперь, когда модель материализована как таблица:
    При желании можете выполнить и другие запросы к этой модели. Например, у каких актёров самые высоко оценённые фильмы среди тех, кто снялся более чем в 5 фильмах?

Создание инкрементальной материализации

В предыдущем примере была создана таблица для материализации модели. Эта таблица будет пересоздаваться при каждом запуске dbt. Для больших результирующих наборов или сложных преобразований это может быть непрактично и чрезвычайно затратно. Чтобы решить эту проблему и сократить время сборки, dbt предлагает инкрементальные материализации. Они позволяют dbt выполнять вставку или обновление записей в таблице с момента последнего запуска, что делает такой подход подходящим для данных событийного типа. Внутри создаётся временная таблица со всеми обновлёнными записями, после чего все неизменённые и обновлённые записи вставляются в новую целевую таблицу. В результате для больших результирующих наборов возникают ограничения, аналогичные ограничениям модели table. Чтобы обойти эти ограничения для больших наборов данных, адаптер поддерживает режим ‘inserts_only’, при котором все обновления вставляются в целевую таблицу без создания временной таблицы (подробнее об этом ниже). Чтобы проиллюстрировать этот пример, мы добавим актёра “Clicky McClickHouse”, который появится в невероятных 910 фильмах, — это гарантирует, что он снялся в большем числе фильмов, чем даже Mel Blanc.
  1. Сначала изменим нашу модель, задав для неё тип incremental. Это требует:
    1. unique_key - Чтобы адаптер мог однозначно идентифицировать строки, необходимо указать unique_key — в данном случае достаточно поля id из нашего запроса. Это гарантирует отсутствие дубликатов строк в нашей материализованной таблице. Подробнее об ограничениях уникальности см. здесь.
    2. Incremental filter - Нам также нужно указать dbt, как определять, какие строки изменились при инкрементальном запуске. Для этого задаётся дельта-выражение. Обычно для данных событий используется временная метка, поэтому мы берём поле updated_at. Этот столбец, которому при вставке строк по умолчанию присваивается значение now(), позволяет выявлять новые роли. Кроме того, нужно учесть альтернативный сценарий, когда добавляются новые акторы. Используя переменную {{this}} для обозначения существующей материализованной таблицы, получаем выражение where id > (select max(id) from {{ this }}) or updated_at > (select max(updated_at) from {{this}}). Мы помещаем его внутрь условия {% if is_incremental() %}, чтобы оно применялось только при инкрементальных запусках, а не при первоначальном создании таблицы. Подробнее о фильтрации строк для инкрементальных моделей см. в этом разделе документации dbt.
    Обновите файл actor_summary.sql следующим образом:
    Обратите внимание, что наша модель будет реагировать только на обновления и добавления в таблицах roles и actors. Чтобы она реагировала на все таблицы, рекомендуется разделить эту модель на несколько подмоделей, каждая из которых будет иметь собственные критерии инкрементальности. На эти модели, в свою очередь, можно ссылаться и связывать их между собой. Дополнительные сведения о перекрёстных ссылках между моделями см. здесь.
  2. Выполните dbt run и проверьте результаты в созданной таблице:
  3. Теперь добавим в нашу модель данные, чтобы показать инкрементное обновление. Добавьте актёра “Clicky McClickHouse” в таблицу actors:
  4. Пусть «Clicky» появится в 910 случайных фильмах:
  5. Подтвердите, что теперь именно он — актёр с наибольшим числом появлений, выполнив запрос напрямую к исходной таблице в обход любых моделей dbt:
  6. Выполните dbt run и убедитесь, что наша модель обновилась и соответствует приведённым выше результатам:

Внутреннее устройство

Мы можем определить, какие команды были выполнены для описанного выше инкрементального обновления, выполнив запрос к журналу запросов ClickHouse.
Скорректируйте приведённый выше запрос под период выполнения. Анализ результатов оставляем пользователю, а здесь выделим общую стратегию, которую адаптер использует для инкрементальных обновлений:
  1. Адаптер создаёт временную таблицу actor_sumary__dbt_tmp. В неё передаются изменившиеся строки.
  2. Создаётся новая таблица actor_summary_new,. Затем строки из старой таблицы переносятся в новую, при этом выполняется проверка, чтобы идентификаторы строк отсутствовали во временной таблице. Это позволяет корректно обрабатывать обновления и дубликаты.
  3. Результаты из временной таблицы переносятся в новую таблицу actor_summary:
  4. Наконец, новая таблица атомарно обменивается со старой версией с помощью оператора EXCHANGE TABLES. После этого старая и временная таблицы удаляются.
Это показано ниже: Эта стратегия может вызывать трудности при работе с очень большими моделями. Подробнее см. в разделе Ограничения.

Стратегия Append (режим только вставки)

Чтобы обойти ограничения, связанные с большими наборами данных в инкрементальных моделях, адаптер использует параметр конфигурации dbt incremental_strategy. Ему можно задать значение append. В этом случае обновленные строки вставляются напрямую в целевую таблицу (то есть imdb_dbt.actor_summary), а временная таблица не создается. Примечание: режим append-only требует, чтобы данные были неизменяемыми или чтобы дубликаты считались допустимыми. Если вам нужна инкрементальная модель таблицы с поддержкой изменяемых строк, не используйте этот режим! Чтобы продемонстрировать этот режим, мы добавим еще одного нового актера и снова выполним dbt run с incremental_strategy='append'.
  1. Настройте режим append-only в actor_summary.sql:
  2. Добавим еще одного известного актера — Danny DeBito
  3. Дадим Danny роли в 920 случайных фильмах.
  4. Выполните dbt run и убедитесь, что Danny был добавлен в таблицу actor_summary
Обратите внимание, насколько быстрее выполнился этот инкрементальный запуск по сравнению со вставкой для “Clicky”. Повторная проверка таблицы query_log показывает различия между двумя инкрементальными запусками:
В этом запуске в таблицу imdb_dbt.actor_summary напрямую добавляются только новые строки, без создания таблицы.

Режим удаления и вставки (экспериментальный)

Изначально в ClickHouse была лишь ограниченная поддержка обновлений и удалений в виде асинхронных Мутаций. Они могут быть чрезвычайно затратными по I/O, поэтому их обычно следует избегать. В ClickHouse 22.8 появились легковесные удаления, а в ClickHouse 25.7 — легковесные обновления. С появлением этих возможностей изменения, вносимые отдельными запросами на обновление, даже при асинхронной материализации становятся мгновенно видимыми для пользователя. Этот режим можно настроить для модели с помощью параметра incremental_strategy, например:
Эта стратегия работает напрямую с таблицей целевой модели, поэтому, если во время выполнения возникнет проблема, данные в инкрементальной модели, скорее всего, окажутся в некорректном состоянии — атомарного обновления здесь нет. Вкратце этот подход выглядит так:
  1. Адаптер создаёт временную таблицу actor_sumary__dbt_tmp. Изменённые строки направляются в эту таблицу.
  2. Для текущей таблицы actor_summary выполняется DELETE. Строки удаляются по id из actor_sumary__dbt_tmp
  3. Строки из actor_sumary__dbt_tmp вставляются в actor_summary с помощью INSERT INTO actor_summary SELECT * FROM actor_sumary__dbt_tmp.
Ниже показан этот процесс:

Режим insert_overwrite (экспериментальный)

Включает следующие шаги:
  1. Создать staging-таблицу (временную таблицу) с той же структурой, что и отношение инкрементальной модели: CREATE TABLE {staging} AS {target}.
  2. Выполнить вставку в staging-таблицу только новых записей (полученных с помощью SELECT).
  3. Заменить в целевой таблице только новые партиции (присутствующие в staging-таблице).

У этого подхода есть следующие преимущества:
  • Он быстрее стратегии по умолчанию, поскольку не копирует всю таблицу.
  • Он безопаснее других стратегий, поскольку не изменяет исходную таблицу, пока операция INSERT не завершится успешно: в случае сбоя на промежуточном этапе исходная таблица не изменяется.
  • Он реализует рекомендуемую в дата-инжиниринге практику «неизменяемости партиций», что упрощает инкрементальную и параллельную обработку данных, откаты и т. д.

Создание снимка

Снимки dbt позволяют сохранять историю изменений изменяемой модели с течением времени. Это, в свою очередь, позволяет выполнять запросы к моделям на определённый момент времени, чтобы аналитики могли «вернуться назад во времени» и посмотреть на предыдущее состояние модели. Это достигается с помощью медленно изменяющихся измерений типа 2, где столбцы с датами начала и окончания фиксируют, в какой период строка была актуальной. Эта функциональность поддерживается адаптером ClickHouse и показана ниже. В этом примере предполагается, что вы уже выполнили шаг Создание инкрементной табличной модели. Убедитесь, что в вашем actor_summary.sql не задано inserts_only=True. Файл models/actor_summary.sql должен выглядеть так:
  1. Создайте файл actor_summary в каталоге snapshots.
  2. Обновите содержимое файла actor_summary.sql следующим образом:
Несколько замечаний по этому содержимому:
  • Запрос select определяет результаты, снимки которых вы хотите сохранять с течением времени. Функция ref используется, чтобы сослаться на ранее созданную модель actor_summary.
  • Нам нужен столбец с временной меткой, чтобы отмечать изменения в записях. Здесь можно использовать наш столбец updated_at (см. Создание инкрементной модели таблицы). Параметр strategy указывает, что для отслеживания обновлений мы используем временную метку, а параметр updated_at задает, какой столбец использовать. Если этого столбца нет в вашей модели, можно вместо этого использовать стратегию check. Это существенно менее эффективно и требует указать список столбцов для сравнения. dbt сравнивает текущие и исторические значения этих столбцов, фиксируя любые изменения (или ничего не делает, если значения совпадают).
  1. Выполните команду dbt snapshot.
Обратите внимание, что в базе данных snapshots была создана таблица actor_summary_snapshot (это задаётся параметром target_schema).
  1. Выбрав эти данные, вы увидите, что dbt добавил столбцы dbt_valid_from и dbt_valid_to. У последнего значения равны null. При последующих запусках это обновится.
  2. Пусть наш любимый актёр Clicky McClickHouse снимется ещё в 10 фильмах.
  3. Снова выполните команду dbt run из каталога imdb. Это обновит инкрементную модель. Когда процесс завершится, выполните dbt snapshot, чтобы зафиксировать изменения.
  4. Если теперь выполнить запрос к нашему снимку, обратите внимание: у нас есть 2 строки для Clicky McClickHouse. В нашей предыдущей записи теперь заполнено значение dbt_valid_to. Новое значение записано с тем же значением в столбце dbt_valid_from, а значение dbt_valid_to равно null. Если бы у нас были новые строки, они также были бы добавлены в снимок.
Подробные сведения о снимках dbt см. здесь.

Использование seed-файлов

dbt предоставляет возможность загружать данные из CSV-файлов. Эта возможность не подходит для загрузки больших выгрузок из базы данных и в большей степени рассчитана на небольшие файлы, обычно используемые для кодовых таблиц и словарей, например для сопоставления кодов стран с названиями стран. В качестве простого примера мы сгенерируем, а затем загрузим список кодов жанров с помощью механизма seed.
  1. Мы генерируем список кодов жанров из имеющегося набора данных. В каталоге dbt используйте clickhouse-client, чтобы создать файл seeds/genre_codes.csv:
  2. Выполните команду dbt seed. Это создаст новую таблицу genre_codes в нашей базе данных imdb_dbt (как задано в конфигурации схемы) со строками из нашего CSV-файла.
  3. Подтвердите, что данные были загружены:

Дополнительная информация

В предыдущих руководствах рассмотрены лишь базовые возможности dbt. Рекомендуем ознакомиться с отличной документацией dbt.
Последнее изменение 2 июля 2026 г.