Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Материализации

Поддерживается в ClickHouse

В этом разделе описаны все материализации, доступные в 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 не полностью поддерживается для распределённых материализаций.

Выполняет следующие шаги:

  1. Создаёт staging-таблицу (временную) с той же структурой, что и отношение инкрементальной модели: CREATE TABLE <staging> AS <target>.
  2. Выполняет вставку только новых записей (созданных SELECT) в staging-таблицу.
  3. Заменяет в целевой таблице только новые партиции (присутствующие в 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 таблица создается следующим образом:

  1. Создается временное представление с SQL-запросом, чтобы получить нужную структуру
  2. Создаются пустые локальные таблицы на основе представления
  3. Создается distributed таблица на основе локальных таблиц.
  4. Данные вставляются в 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 таблица; основная сложность заключается в корректной обработке всех инкрементальных стратегий.

  1. Стратегия Append просто выполняет вставку данных в distributed таблицу.
  2. Стратегия Delete+Insert создает временную distributed таблицу для работы со всеми данными на каждом сегменте.
  3. Стратегия 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.)

Navigation