В этом разделе описаны все материализации, доступные в dbt-clickhouse, включая экспериментальные возможности.
Общие конфигурации материализаций
В следующей таблице показаны конфигурации, общие для некоторых доступных материализаций. Подробную информацию об общих конфигурациях моделей dbt см. в документации dbt:
| Option | Description | Default if any |
|---|---|---|
| engine | Движок таблицы (тип таблицы), который используется при создании таблиц | MergeTree() |
| order_by | Кортеж имён столбцов или произвольных выражений. Это позволяет создать небольшой разреженный индекс, который помогает быстрее находить данные. | tuple() |
| partition_by | Партиция — это логическое объединение записей в таблице по заданному критерию. Ключом партиционирования может быть любое выражение на основе столбцов таблицы. | |
| primary_key | Как и order_by, это выражение первичного ключа ClickHouse. Если оно не указано, ClickHouse будет использовать выражение order_by в качестве первичного ключа | |
| settings | Словарь настроек "TABLE", который используется с DDL-операторами, такими как 'CREATE TABLE', для этой модели | |
| query_settings | Словарь пользовательских настроек уровня пользователя ClickHouse, который используется с операторами INSERT или DELETE вместе с этой моделью |
|
| ttl | Выражение TTL, используемое с таблицей. Выражение TTL представляет собой строку, с помощью которой можно задать TTL для таблицы. | |
| sql_security | Пользователь ClickHouse, которого следует использовать при выполнении запроса, лежащего в основе представления. Допустимые значения: definer, invoker. |
|
| definer | Если для sql_security установлено значение definer, необходимо указать любого существующего пользователя или CURRENT_USER в предложении definer. |
Поддерживаемые движки таблиц
| Тип | Подробности |
|---|---|
| MergeTree (по умолчанию) | документация. |
| HDFS | документация |
| MaterializedPostgreSQL | документация |
| S3 | документация |
| EmbeddedRocksDB | документация |
| Hive | документация |
Примечание: для материализованных представлений поддерживаются все движки *MergeTree.
Экспериментально поддерживаемые движки таблиц
| Тип | Подробности |
|---|---|
| Distributed таблица | документация. |
| словарь | документация |
Если при подключении dbt к ClickHouse с использованием одного из указанных выше движков у вас возникают проблемы, сообщите о них здесь.
Примечание о настройках модели
В ClickHouse есть несколько типов/уровней «настроек». В приведенной выше конфигурации модели можно настраивать два их типа.
settings означает предложение SETTINGS,
используемое в DDL-операторах типа CREATE TABLE/VIEW, то есть обычно это настройки, специфичные для
конкретного движка таблицы ClickHouse. Новый
query_settings используется для добавления предложения SETTINGS в запросы INSERT и DELETE, применяемые при материализации модели (
включая инкрементные материализации).
Существуют сотни настроек ClickHouse, и не всегда очевидно, какая из них является настройкой таблицы, а какая — настройкой пользователя
(хотя последние, как правило,
доступны в таблице system.settings.) В целом рекомендуется использовать значения по умолчанию, а к использованию этих свойств
следует подходить только после тщательного изучения и тестирования.
Конфигурация столбца
ПРИМЕЧАНИЕ: Чтобы использовать указанные ниже параметры конфигурации столбца, необходимо включить контракты моделей.
| Параметр | Описание | Значение по умолчанию, если есть |
|---|---|---|
| codec | Строка, содержащая аргументы, передаваемые в CODEC() в DDL столбца. Например: codec: "Delta, ZSTD" будет скомпилировано как CODEC(Delta, ZSTD). |
|
| ttl | Строка, содержащая TTL-выражение (time-to-live), которое задаёт правило TTL в DDL столбца. Например: ttl: ts + INTERVAL 1 DAY будет скомпилировано как TTL ts + INTERVAL 1 DAY. |
Пример конфигурации схемы
models:
- name: table_column_configs
description: 'Testing column-level configurations'
config:
contract:
enforced: true
columns:
- name: ts
data_type: timestamp
codec: ZSTD
- name: x
data_type: UInt8
ttl: ts + INTERVAL 1 DAYДобавление сложных типов
dbt автоматически определяет тип данных каждого столбца, анализируя SQL, используемый для создания модели. Однако в некоторых случаях этот процесс может определять тип данных неточно, что приводит к конфликтам с типами, указанными в свойстве data_type контракта. Чтобы избежать этого, мы рекомендуем использовать функцию CAST() в SQL модели, чтобы явно указать нужный тип. Например:
{{
config(
materialized="materialized_view",
engine="AggregatingMergeTree",
order_by=["event_type"],
)
}}
select
-- event_type может быть определён как String, но предпочтительнее использовать LowCardinality(String):
CAST(event_type, 'LowCardinality(String)') as event_type,
-- countState() может быть определён как `AggregateFunction(count)`, но может потребоваться изменить тип используемого аргумента:
CAST(countState(), 'AggregateFunction(count, UInt32)') as response_count,
-- maxSimpleState() может быть определён как `SimpleAggregateFunction(max, String)`, но может также потребоваться изменить тип используемого аргумента:
CAST(maxSimpleState(event_type), 'SimpleAggregateFunction(max, LowCardinality(String))') as max_event_type
from {{ ref('user_events') }}
group by event_typeМатериализация: представление
Модель dbt можно создать как представление ClickHouse и настроить, используя следующий синтаксис:
Файл проекта (dbt_project.yml):
models:
<resource-path>:
+materialized: viewИли блок config (models/<model_name>.sql):
{{ config(materialized = "view") }}Материализация: таблица
Модель dbt можно создать в виде таблицы ClickHouse и настроить, используя следующий синтаксис:
Файл проекта (dbt_project.yml):
models:
<resource-path>:
+materialized: table
+order_by: [ <column-name>, ... ]
+engine: <engine-type>
+partition_by: [ <column-name>, ... ]Или блок config (models/<model_name>.sql):
{{ config(
materialized = "table",
engine = "<engine-type>",
order_by = [ "<column-name>", ... ],
partition_by = [ "<column-name>", ... ],
...
]
) }}Индексы пропуска данных
Вы можете добавлять индексы пропуска данных к материализациям table, используя конфигурацию indexes:
{{ config(
materialized='table',
indexes=[{
'name': 'your_index_name',
'definition': 'your_column TYPE minmax GRANULARITY 2'
}]
) }}Проекции
Вы можете добавлять проекции в материализации table и distributed_table с помощью конфигурации projections. Для каждой записи проекции требуется ключ query или index (но не оба).
Примечание: Для distributed таблиц проекция применяется к таблицам _local, а не к прокси-таблице distributed.
Примечание: Указание одновременно query и index в одной записи проекции вызывает ошибку на этапе компиляции.
Проекции запросов
Используйте query, чтобы задать полный запрос проекции:
{{ config(
materialized='table',
projections=[
{
'name': 'your_projection_name',
'query': 'SELECT department, avg(age) AS avg_age GROUP BY department'
}
]
) }}Индексные проекции
Используйте index как синтаксический сахар для легковесных индексных проекций, использующих виртуальный столбец _part_offset. В качестве ключа сортировки укажите имя одного столбца или список столбцов:
{{ config(
materialized='table',
projections=[
{
'name': 'proj_by_age',
'index': 'age'
}
]
) }}{{ config(
materialized='table',
projections=[
{
'name': 'proj_by_dept_age',
'index': ['department', 'age']
}
]
) }}dbt-clickhouse автоматически генерирует DDL с учётом версии:
| Версия ClickHouse | Сгенерированный SQL |
|---|---|
| 26.1+ | ADD PROJECTION proj_by_age INDEX age TYPE basic |
| 25.8 – 26.0 | ADD PROJECTION proj_by_age (SELECT _part_offset ORDER BY age) |
Материализация: инкрементальная
Модель типа table будет пересоздаваться при каждом запуске dbt. Это может оказаться непрактичным и чрезвычайно затратным для больших результирующих наборов или сложных преобразований. Чтобы решить эту проблему и сократить время сборки, модель dbt можно создать как инкрементальную таблицу ClickHouse и настроить с помощью следующего синтаксиса:
Определение модели в dbt_project.yml:
models:
<resource-path>:
+materialized: incremental
+order_by: [ <column-name>, ... ]
+engine: <engine-type>
+partition_by: [ <column-name>, ... ]
+unique_key: [ <column-name>, ... ]
+inserts_only: [ True|False ]Или блок config в models/<model_name>.sql:
{{ config(
materialized = "incremental",
engine = "<engine-type>",
order_by = [ "<column-name>", ... ],
partition_by = [ "<column-name>", ... ],
unique_key = [ "<column-name>", ... ],
inserts_only = [ True|False ],
...
]
) }}Конфигурации
Ниже перечислены конфигурации, характерные для этого типа материализации:
| Параметр | Описание | Обязательно? |
|---|---|---|
unique_key |
Кортеж имён столбцов, которые однозначно идентифицируют строки. Подробнее об ограничениях уникальности см. здесь. | Обязательно. Если не указать, изменённые строки будут дважды добавлены в инкрементальную таблицу. |
inserts_only |
Этот параметр устарел в пользу инкрементальной strategy append, которая работает аналогичным образом. Если для инкрементальной модели задано значение True, инкрементальные обновления будут вставляться напрямую в целевую таблицу без создания промежуточной таблицы. Если задан inserts_only, incremental_strategy игнорируется. |
Необязательно (по умолчанию: False) |
incremental_strategy |
Стратегия, используемая для инкрементальной материализации. Поддерживаются delete+insert, append, insert_overwrite и microbatch. Дополнительные сведения о стратегиях см. здесь |
Необязательно (по умолчанию: 'default') |
incremental_predicates |
Дополнительные условия, применяемые к инкрементальной материализации (только для стратегии delete+insert |
Необязательно |
Стратегии инкрементальных моделей
dbt-clickhouse поддерживает три стратегии для инкрементальных моделей.
Стратегия по умолчанию (устаревшая)
Исторически ClickHouse поддерживал обновления и удаления лишь ограниченно — в виде асинхронных «мутаций». Чтобы эмулировать ожидаемое поведение dbt, dbt-clickhouse по умолчанию создает новую временную таблицу, содержащую все незатронутые (не удаленные и не измененные) «старые» записи, а также все новые или обновленные записи, а затем меняет местами или выполняет EXCHANGE этой временной таблицы с существующим отношением инкрементальной модели. Это единственная стратегия, которая сохраняет исходное отношение, если что-то пойдет не так до завершения операции; однако, поскольку она требует полного копирования исходной таблицы, ее выполнение может быть довольно дорогим и медленным.
Стратегия Delete+Insert
Стратегия delete+insert использует легковесное удаление, чтобы удалить затронутые строки, а затем вставить новые. Поскольку она не копирует всю таблицу, её производительность значительно выше, чем у стратегии «legacy». Если в профиле задать use_lw_deletes: true, delete+insert станет инкрементальной стратегией по умолчанию.
При использовании этой стратегии следует учитывать несколько важных ограничений:
- Она работает непосредственно с затронутой таблицей, не создавая промежуточных или временных таблиц, поэтому при возникновении проблемы во время операции данные в инкрементальной модели, скорее всего, окажутся в некорректном состоянии.
- Для неё требуется настройка ClickHouse
allow_nondeterministic_mutations. Адаптер автоматически включает её в своих сеансах, когда это возможно. Если включить её нельзя (например, она доступна пользователю dbt только для чтения), поведение зависит от способа выбора стратегии: модели, использующие стратегию по умолчанию, незаметно переключаются на стратегию legacy, модели, в которых явно заданаdelete+insertилиmicrobatch, завершаются ошибкой во время выполнения, аuse_lw_deletes: trueв профиле вызывает ошибку при подключении. - В некоторых крайне редких случаях использование недетерминированных
incremental_predicatesможет привести к состоянию гонки для обновляемых или удаляемых элементов. Чтобы обеспечить согласованные результаты, инкрементальные предикаты должны включать только подзапросы к данным, которые не будут изменяться во время инкрементальной материализации.
Стратегия Microbatch (требуется dbt-core >= 1.9)
Инкрементальная стратегия microbatch доступна в dbt-core начиная с версии 1.9 и предназначена для эффективной обработки масштабных преобразований временных рядов. В dbt-clickhouse она основана на существующей инкрементальной стратегии delete_insert, разбивая инкрементальную обработку на заранее определённые батчи временных рядов на основе конфигураций модели event_time и batch_size.
Помимо обработки масштабных преобразований, Microbatch позволяет:
- Повторно обрабатывать неуспешные батчи.
- Автоматически определять параллельное выполнение батчей.
- Избавиться от необходимости в сложной условной логике при дозагрузке.
Подробные сведения об использовании Microbatch см. в официальной документации.
Доступные конфигурации Microbatch
| Option | Description | Default if any |
|---|---|---|
| event_time | Столбец, указывающий, «в какое время произошла строка». Обязателен для вашей модели Microbatch и всех непосредственных родительских моделей, к которым должна применяться фильтрация. | |
| begin | «Начало времён» для модели Microbatch. Это отправная точка для любых первоначальных или full-refresh сборок. Например, если ежедневная модель Microbatch запущена 2024-10-01 с begin = '2023-10-01', будет обработано 366 батчей (это високосный год!) плюс батч за «сегодня». |
|
| batch_size | Гранулярность батчей. Поддерживаемые значения: hour, day, month и year |
|
| lookback | Обрабатывает X батчей перед последней закладкой, чтобы захватить записи, поступившие с задержкой. | 1 |
| concurrent_batches | Переопределяет автоматически определённое dbt поведение для параллельного выполнения батчей. Подробнее см. настройку concurrent batches. Значение true запускает батчи параллельно (одновременно), а false — последовательно (один за другим). |
Стратегия Append
Эта стратегия заменяет настройку inserts_only в предыдущих версиях dbt-clickhouse. При таком подходе новые строки просто добавляются
в существующее отношение.
В результате дубликаты строк не удаляются, и временная или промежуточная таблица не используется. Это самый быстрый
подход, если дубликаты либо допустимы
в данных, либо исключаются предложением WHERE/фильтром в инкрементальном запросе.
Стратегия insert_overwrite (экспериментальная)
[IMPORTANT] В настоящее время стратегия insert_overwrite не полностью поддерживается для распределённых материализаций.
Выполняет следующие шаги:
- Создаёт staging-таблицу (временную) с той же структурой, что и отношение инкрементальной модели:
CREATE TABLE <staging> AS <target>. - Выполняет вставку только новых записей (созданных
SELECT) в staging-таблицу. - Заменяет в целевой таблице только новые партиции (присутствующие в staging-таблице).
У этого подхода есть следующие преимущества:
- Он быстрее стратегии по умолчанию, потому что не копирует таблицу целиком.
- Он безопаснее других стратегий, потому что не изменяет исходную таблицу, пока операция INSERT не завершится успешно: в случае сбоя на промежуточном этапе исходная таблица не изменяется.
- Он реализует лучшую практику data engineering — «неизменяемость партиций». Это упрощает инкрементальную и параллельную обработку данных, откаты и т. д.
Для этой стратегии в config модели должен быть задан partition_by. Все остальные параметры config модели,
специфичные для стратегии, игнорируются.
Материализация: materialized_view
Материализация materialized_view создаёт в ClickHouse materialized view, который служит триггером вставки: он автоматически преобразует и вставляет новые строки из исходной таблицы в целевую таблицу. Это одна из самых мощных материализаций в dbt-clickhouse.
Из-за объёма материала эта материализация вынесена на отдельную страницу. Перейдите к руководству по Materialized Views, чтобы ознакомиться с полной документацией
Материализация: словарь (экспериментальный)
Модель dbt можно создать в виде словаря ClickHouse. При каждом запуске dbt run словарь заменяется текущим определением модели с помощью CREATE OR REPLACE DICTIONARY.
Конфигурации
| Параметр | Описание | Обязательно |
|---|---|---|
fields |
Структура словаря в виде списка пар (name, type). |
Да |
primary_key |
Первичный ключ словаря. Должен соответствовать типу ключа, ожидаемому выбранной структурой (например, составной ключ для структур COMPLEX_KEY_*). |
Да |
layout |
Структура, используемая для хранения словаря в памяти, например HASHED(), COMPLEX_KEY_HASHED() или DIRECT(). |
Да |
source_type |
Источник данных словаря: clickhouse (по умолчанию использует SQL модели или параметр table) либо http. |
|
lifetime |
Предложение LIFETIME, определяющее частоту обновления словаря, например MIN 0 MAX 300. Необязательно начиная с dbt-clickhouse 1.10.0 — не указывайте его для структур, которые его не используют, например DIRECT(). |
|
table |
Только для источника clickhouse. Чтение из существующей таблицы вместо SQL модели. |
|
update_field |
Только для источника clickhouse. Позволяет обновлять словарь инкрементально, получая только строки, значение в этом столбце которых изменилось с момента предыдущего обновления. См. LIFETIME. Доступно начиная с dbt-clickhouse 1.10.0. |
|
update_lag |
Только для источника clickhouse. Количество секунд, вычитаемое из времени предыдущего обновления при использовании update_field, чтобы учесть обновления, поступившие с задержкой. Доступно начиная с dbt-clickhouse 1.10.0. |
|
connection_overrides |
Только для источника clickhouse. Переопределения учётных данных, используемых в предложении SOURCE словаря, например {'user': 'dictionary_reader'}. |
|
url, format |
Только для источника http. URL исходного файла и его входной формат. |
Да для http |
range |
Предложение RANGE для структур RANGE_HASHED(), например 'min start max stop'. |
Пример с источником данных ClickHouse
SQL модели становится запросом к источнику словаря:
{{ config(
materialized='dictionary',
fields=[
('id', 'UInt64'),
('name', 'String'),
],
primary_key='id',
layout='HASHED()',
lifetime='MIN 0 MAX 300'
) }}
select id, name from {{ source('raw', 'people') }}Пример с HTTP-источником
При использовании source_type='http' (или параметра table) источником служит не SQL модели, однако dbt по-прежнему требует тело запроса — используйте в качестве заполнителя select 1:
{{ config(
materialized='dictionary',
fields=[
('LocationID', 'UInt16 DEFAULT 0'),
('Borough', 'String'),
('Zone', 'String'),
],
primary_key='LocationID',
layout='HASHED()',
lifetime='MIN 0 MAX 0',
source_type='http',
url='https://datasets-documentation.s3.eu-west-3.amazonaws.com/nyc-taxi/taxi_zone_lookup.csv',
format='CSVWithNames'
) }}
select 1Дополнительные примеры, в том числе словарей с разметкой range и direct, см. в тестах словарей.
Материализация: distributed_table (экспериментальная)
distributed таблица создается следующим образом:
- Создается временное представление с SQL-запросом, чтобы получить нужную структуру
- Создаются пустые локальные таблицы на основе представления
- Создается distributed таблица на основе локальных таблиц.
- Данные вставляются в distributed таблицу и распределяются по сегментам без дублирования.
Примечания:
- Запросы dbt-clickhouse теперь автоматически включают настройку
insert_distributed_sync = 1, чтобы последующие операции инкрементальной материализации выполнялись корректно. Из-за этого некоторые вставки в distributed таблицу могут выполняться медленнее, чем ожидалось.
Пример модели для distributed таблицы
{{
config(
materialized='distributed_table',
order_by='id, created_at',
sharding_key='cityHash64(id)',
engine='ReplacingMergeTree'
)
}}
select id, created_at, item
from {{ source('db', 'table') }}Сгенерированные миграции
CREATE TABLE db.table_local on cluster cluster (
`id` UInt64,
`created_at` DateTime,
`item` String
)
ENGINE = ReplacingMergeTree
ORDER BY (id, created_at);
CREATE TABLE db.table on cluster cluster (
`id` UInt64,
`created_at` DateTime,
`item` String
)
ENGINE = Distributed ('cluster', 'db', 'table_local', cityHash64(id));Конфигурации
Ниже перечислены конфигурации, специфичные для этого типа материализации:
| Параметр | Описание | Значение по умолчанию, если есть |
|---|---|---|
| sharding_key | Ключ сегментирования определяет сервер назначения при вставке в таблицу с движком Distributed. Ключ сегментирования может быть случайным или представлять собой результат работы хеш-функции | rand()) |
материализация: distributed_incremental (экспериментальная)
Инкрементальная модель, основанная на той же идее, что и distributed таблица; основная сложность заключается в корректной обработке всех инкрементальных стратегий.
- Стратегия Append просто выполняет вставку данных в distributed таблицу.
- Стратегия Delete+Insert создает временную distributed таблицу для работы со всеми данными на каждом сегменте.
- Стратегия Default (Legacy) создает временную и промежуточную distributed таблицы по той же причине.
Заменяются только таблицы сегментов, поскольку distributed таблица не хранит данные. Distributed таблица перезагружается только при включенном режиме full_refresh или если структура таблицы могла измениться.
Пример инкрементальной модели Distributed
{{
config(
materialized='distributed_incremental',
engine='MergeTree',
incremental_strategy='append',
unique_key='id,created_at'
)
}}
select id, created_at, item
from {{ source('db', 'table') }}Созданные миграции
CREATE TABLE db.table_local on cluster cluster (
`id` UInt64,
`created_at` DateTime,
`item` String
)
ENGINE = MergeTree;
CREATE TABLE db.table on cluster cluster (
`id` UInt64,
`created_at` DateTime,
`item` String
)
ENGINE = Distributed ('cluster', 'db', 'table_local', cityHash64(id));Снимок
Снимки dbt позволяют отслеживать изменения в изменяемой модели с течением времени. Это, в свою очередь, позволяет выполнять запросы к моделям на определённый момент времени, при которых аналитики могут "заглянуть в прошлое" и увидеть предыдущее состояние модели. Эта функциональность поддерживается коннектором ClickHouse и настраивается с помощью следующего синтаксиса:
Блок config в snapshots/<model_name>.sql:
{{
config(
schema = "<schema-name>",
unique_key = "<column-name>",
strategy = "<strategy>",
updated_at = "<updated-at-column-name>",
)
}}Чтобы узнать больше о конфигурации, см. справочную страницу по конфигурациям снимков.
Контракты и ограничения
Поддерживаются только контракты, в которых типы столбцов должны точно совпадать. Например, контракт с типом столбца UInt32 завершится ошибкой, если модель
возвращает UInt64 или другой целочисленный тип.
ClickHouse также поддерживает только ограничения CHECK для всей таблицы/модели. Первичный ключ, внешний ключ, уникальные ограничения и
ограничения CHECK на уровне столбца не поддерживаются.
(См. документацию ClickHouse о первичных ключах и ключах ORDER BY.)