- Создание проекта dbt и настройка адаптера ClickHouse.
- Определение модели.
- Обновление модели.
- Создание инкрементальной модели.
- Создание модели-снимка.
- Использование materialized views.
Настройка
Подготовьте ClickHouse
Столбец
created_at в таблице roles; по умолчанию для него задано значение now(). Позже мы используем его, чтобы определять инкрементальные обновления наших моделей — см. Инкрементальные модели.s3, чтобы читать исходные данные из общедоступных конечных точек и выполнять вставку данных. Выполните следующие команды, чтобы заполнить таблицы:
Подключение к ClickHouse
-
Создайте проект dbt. В этом случае мы назовём его по имени нашего источника
imdb. Когда появится запрос, выберитеclickhouseв качестве источника базы данных. -
Перейдите в каталог проекта с помощью
cd: - На этом этапе вам понадобится любой текстовый редактор. В примерах ниже мы используем популярный VS Code. Открыв каталог IMDB, вы должны увидеть набор файлов yml и sql:
-
Обновите файл
dbt_project.yml, чтобы указать нашу первую модель —actor_summary, и задайте профильclickhouse_imdb. -
Далее нужно указать для dbt сведения о подключении к вашему экземпляру ClickHouse. Добавьте следующее в
~/.dbt/profiles.yml.Обратите внимание: нужно изменить имя пользователя и пароль. Дополнительные доступные настройки описаны здесь. -
Находясь в каталоге IMDB, выполните команду
dbt debug, чтобы проверить, может ли dbt подключиться к ClickHouse.Убедитесь, что в выводе есть строкаConnection test: [OK connection ok], которая указывает на успешное подключение.
Создание простой материализации представления
CREATE VIEW AS в ClickHouse. Это не требует дополнительного хранения данных, но запросы к такому представлению будут выполняться медленнее, чем при материализации в таблицы.
-
В папке
imdbудалите каталогmodels/example: -
Создайте новый файл в каталоге
actorsвнутри папкиmodels. Здесь мы создаем файлы, каждый из которых соответствует отдельной модели actor: -
Создайте файлы
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. Позже он понадобится для инкрементальных материализаций. -
В каталоге
imdbвыполните командуdbt run. -
dbt представит модель как представление в ClickHouse, как и было запрошено. Теперь мы можем выполнять запросы к этому представлению напрямую. Это представление будет создано в базе данных
imdb_dbt— это определяется параметромschemaв файле~/.dbt/profiles.ymlв профилеclickhouse_imdb.Выполнив запрос к этому представлению, мы можем получить те же результаты, что и в предыдущем запросе, но с более простым синтаксисом:
Создание материализации в таблицу
INSERT TO SELECT. Обратите внимание, что эта таблица будет пересоздаваться каждый раз, то есть она не является инкрементальной. Поэтому большие результирующие наборы могут приводить к длительному времени выполнения — см. Ограничения dbt.
-
Измените файл
actors_summary.sql, чтобы параметрmaterializedбыл установлен вtable. Обратите внимание, как заданORDER BY, а также на то, что мы используем движок таблицыMergeTree: -
Из каталога
imdbвыполните командуdbt run. Выполнение может занять немного больше времени — около 10 с на большинстве машин. -
Подтвердите создание таблицы
imdb_dbt.actor_summary:Вы должны увидеть таблицу с соответствующими типами данных: -
Убедитесь, что результаты из этой таблицы совпадают с предыдущими результатами. Обратите внимание на заметное улучшение времени отклика теперь, когда модель материализована как таблица:
При желании можете выполнить и другие запросы к этой модели. Например, у каких актёров самые высоко оценённые фильмы среди тех, кто снялся более чем в 5 фильмах?
Создание инкрементальной материализации
-
Сначала изменим нашу модель, задав для неё тип incremental. Это требует:
- unique_key - Чтобы адаптер мог однозначно идентифицировать строки, необходимо указать unique_key — в данном случае достаточно поля
idиз нашего запроса. Это гарантирует отсутствие дубликатов строк в нашей материализованной таблице. Подробнее об ограничениях уникальности см. здесь. - 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. Чтобы она реагировала на все таблицы, рекомендуется разделить эту модель на несколько подмоделей, каждая из которых будет иметь собственные критерии инкрементальности. На эти модели, в свою очередь, можно ссылаться и связывать их между собой. Дополнительные сведения о перекрёстных ссылках между моделями см. здесь. - unique_key - Чтобы адаптер мог однозначно идентифицировать строки, необходимо указать unique_key — в данном случае достаточно поля
-
Выполните
dbt runи проверьте результаты в созданной таблице: -
Теперь добавим в нашу модель данные, чтобы показать инкрементное обновление. Добавьте актёра “Clicky McClickHouse” в таблицу
actors: -
Пусть «Clicky» появится в 910 случайных фильмах:
-
Подтвердите, что теперь именно он — актёр с наибольшим числом появлений, выполнив запрос напрямую к исходной таблице в обход любых моделей dbt:
-
Выполните
dbt runи убедитесь, что наша модель обновилась и соответствует приведённым выше результатам:
Внутреннее устройство
- Адаптер создаёт временную таблицу
actor_sumary__dbt_tmp. В неё передаются изменившиеся строки. - Создаётся новая таблица
actor_summary_new,. Затем строки из старой таблицы переносятся в новую, при этом выполняется проверка, чтобы идентификаторы строк отсутствовали во временной таблице. Это позволяет корректно обрабатывать обновления и дубликаты. - Результаты из временной таблицы переносятся в новую таблицу
actor_summary: - Наконец, новая таблица атомарно обменивается со старой версией с помощью оператора
EXCHANGE TABLES. После этого старая и временная таблицы удаляются.
Стратегия Append (режим только вставки)
incremental_strategy. Ему можно задать значение append. В этом случае обновленные строки вставляются напрямую в целевую таблицу (то есть imdb_dbt.actor_summary), а временная таблица не создается.
Примечание: режим append-only требует, чтобы данные были неизменяемыми или чтобы дубликаты считались допустимыми. Если вам нужна инкрементальная модель таблицы с поддержкой изменяемых строк, не используйте этот режим!
Чтобы продемонстрировать этот режим, мы добавим еще одного нового актера и снова выполним dbt run с incremental_strategy='append'.
-
Настройте режим append-only в actor_summary.sql:
-
Добавим еще одного известного актера — Danny DeBito
-
Дадим Danny роли в 920 случайных фильмах.
-
Выполните
dbt runи убедитесь, что Danny был добавлен в таблицуactor_summary
imdb_dbt.actor_summary напрямую добавляются только новые строки, без создания таблицы.
Режим удаления и вставки (экспериментальный)
incremental_strategy, например:
- Адаптер создаёт временную таблицу
actor_sumary__dbt_tmp. Изменённые строки направляются в эту таблицу. - Для текущей таблицы
actor_summaryвыполняетсяDELETE. Строки удаляются по id изactor_sumary__dbt_tmp - Строки из
actor_sumary__dbt_tmpвставляются вactor_summaryс помощьюINSERT INTO actor_summary SELECT * FROM actor_sumary__dbt_tmp.
Режим insert_overwrite (экспериментальный)
- Создать staging-таблицу (временную таблицу) с той же структурой, что и отношение инкрементальной модели:
CREATE TABLE {staging} AS {target}. - Выполнить вставку в staging-таблицу только новых записей (полученных с помощью SELECT).
- Заменить в целевой таблице только новые партиции (присутствующие в staging-таблице).
У этого подхода есть следующие преимущества:
- Он быстрее стратегии по умолчанию, поскольку не копирует всю таблицу.
- Он безопаснее других стратегий, поскольку не изменяет исходную таблицу, пока операция INSERT не завершится успешно: в случае сбоя на промежуточном этапе исходная таблица не изменяется.
- Он реализует рекомендуемую в дата-инжиниринге практику «неизменяемости партиций», что упрощает инкрементальную и параллельную обработку данных, откаты и т. д.
Создание снимка
-
Создайте файл
actor_summaryв каталоге snapshots. -
Обновите содержимое файла actor_summary.sql следующим образом:
- Запрос
selectопределяет результаты, снимки которых вы хотите сохранять с течением времени. Функция ref используется, чтобы сослаться на ранее созданную модель actor_summary. - Нам нужен столбец с временной меткой, чтобы отмечать изменения в записях. Здесь можно использовать наш столбец updated_at (см. Создание инкрементной модели таблицы). Параметр strategy указывает, что для отслеживания обновлений мы используем временную метку, а параметр updated_at задает, какой столбец использовать. Если этого столбца нет в вашей модели, можно вместо этого использовать стратегию check. Это существенно менее эффективно и требует указать список столбцов для сравнения. dbt сравнивает текущие и исторические значения этих столбцов, фиксируя любые изменения (или ничего не делает, если значения совпадают).
-
Выполните команду
dbt snapshot.
-
Выбрав эти данные, вы увидите, что dbt добавил столбцы dbt_valid_from и dbt_valid_to. У последнего значения равны null. При последующих запусках это обновится.
-
Пусть наш любимый актёр Clicky McClickHouse снимется ещё в 10 фильмах.
-
Снова выполните команду dbt run из каталога
imdb. Это обновит инкрементную модель. Когда процесс завершится, выполните dbt snapshot, чтобы зафиксировать изменения. -
Если теперь выполнить запрос к нашему снимку, обратите внимание: у нас есть 2 строки для Clicky McClickHouse. В нашей предыдущей записи теперь заполнено значение dbt_valid_to. Новое значение записано с тем же значением в столбце dbt_valid_from, а значение dbt_valid_to равно null. Если бы у нас были новые строки, они также были бы добавлены в снимок.
Использование seed-файлов
-
Мы генерируем список кодов жанров из имеющегося набора данных. В каталоге dbt используйте
clickhouse-client, чтобы создать файлseeds/genre_codes.csv: -
Выполните команду
dbt seed. Это создаст новую таблицуgenre_codesв нашей базе данныхimdb_dbt(как задано в конфигурации схемы) со строками из нашего CSV-файла. -
Подтвердите, что данные были загружены: