Skip to content
ClickHouse Docs
ClickHouse DocsClickHouse Docs

Materializações

Suportado pelo ClickHouse

Esta seção apresenta todas as materializações disponíveis no dbt-clickhouse, incluindo recursos experimentais.

Configurações gerais de materialização

A tabela a seguir mostra as configurações compartilhadas por algumas das materializações disponíveis. Para informações mais detalhadas sobre configurações gerais de modelos do dbt, consulte a documentação do dbt:

Option Description Default if any
engine O motor de tabela (tipo de tabela) a ser usado ao criar tabelas MergeTree()
order_by Uma tupla de nomes de colunas ou expressões arbitrárias. Isso permite criar um pequeno índice esparso que ajuda a localizar dados mais rapidamente. tuple()
partition_by Uma partição é uma combinação lógica de registros em uma tabela com base em um critério especificado. A chave de partição pode ser qualquer expressão das colunas da tabela.
primary_key Assim como em order_by, uma expressão de chave primária do ClickHouse. Se não for especificada, o ClickHouse usará a expressão de ordenação como chave primária
settings Um mapa/dicionário de configurações de "TABLE" para uso em instruções DDL, como CREATE TABLE, com este modelo
query_settings Um mapa/dicionário de configurações em nível de usuário do ClickHouse para uso com instruções INSERT ou DELETE em conjunto com este modelo
ttl Uma expressão TTL a ser usada com a tabela. A expressão TTL é uma string que pode ser usada para especificar o TTL da tabela.
sql_security O usuário do ClickHouse a ser usado ao executar a consulta subjacente da view. Valores aceitos: definer, invoker.
definer Se sql_security tiver sido definido como definer, você deverá especificar qualquer usuário existente ou CURRENT_USER na cláusula definer.

Motores de tabela compatíveis

Tipo Detalhes
MergeTree (padrão) documentação.
HDFS documentação
MaterializedPostgreSQL documentação
S3 documentação
EmbeddedRocksDB documentação
Hive documentação

Observação: para visões materializadas, todos os motores da família *MergeTree são compatíveis.

Motores de tabela com suporte experimental

Tipo Detalhes
Tabela distribuída documentação.
Dicionário documentação

Se você encontrar problemas para se conectar ao ClickHouse no dbt com um dos motores acima, abra uma issue aqui.

Uma observação sobre as configurações do modelo

O ClickHouse tem vários tipos/níveis de "configurações". Na configuração do modelo acima, dois desses tipos são configuráveis. settings se refere à cláusula SETTINGS usada em instruções DDL do tipo CREATE TABLE/VIEW, portanto, em geral, são configurações específicas do motor de tabela do ClickHouse em questão. O novo query_settings é usado para adicionar uma cláusula SETTINGS às consultas INSERT e DELETE usadas na materialização do modelo ( incluindo materializações incrementais). Há centenas de configurações no ClickHouse, e nem sempre é claro qual é uma configuração de "tabela" e qual é uma configuração de "usuário" (embora estas últimas geralmente estejam disponíveis na tabela system.settings.) Em geral, recomenda-se usar os valores padrão, e qualquer uso dessas propriedades deve ser cuidadosamente pesquisado e testado.

Configuração de coluna

OBSERVAÇÃO: As opções de configuração de coluna abaixo exigem que os contratos de modelo estejam habilitados.

Opção Descrição Padrão, se houver
codec Uma string com os argumentos passados para CODEC() na DDL da coluna. Por exemplo: codec: "Delta, ZSTD" será compilado como CODEC(Delta, ZSTD).
ttl Uma string com uma expressão de TTL (time-to-live) que define uma regra de TTL na DDL da coluna. Por exemplo: ttl: ts + INTERVAL 1 DAY será compilado como TTL ts + INTERVAL 1 DAY.

Exemplo de configuração de esquema

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

Adicionando tipos complexos

O dbt determina automaticamente o tipo de dado de cada coluna ao analisar o SQL usado para criar o modelo. No entanto, em alguns casos, esse processo pode não identificar corretamente o tipo de dado, o que gera conflitos com os tipos especificados na propriedade de contrato data_type. Para resolver isso, recomendamos usar a função CAST() no SQL do modelo para definir explicitamente o tipo desejado. Por exemplo:

{{
    config(
        materialized="materialized_view",
        engine="AggregatingMergeTree",
        order_by=["event_type"],
    )
}}

select
  -- event_type pode ser inferido como String, mas podemos preferir LowCardinality(String):
  CAST(event_type, 'LowCardinality(String)') as event_type,
  -- countState() pode ser inferido como `AggregateFunction(count)`, mas podemos preferir alterar o tipo do argumento utilizado:
  CAST(countState(), 'AggregateFunction(count, UInt32)') as response_count, 
  -- maxSimpleState() pode ser inferido como `SimpleAggregateFunction(max, String)`, mas podemos preferir também alterar o tipo do argumento utilizado:
  CAST(maxSimpleState(event_type), 'SimpleAggregateFunction(max, LowCardinality(String))') as max_event_type
from {{ ref('user_events') }}
group by event_type

Materialização: view

Um modelo do dbt pode ser criado como uma view no ClickHouse e configurado com a seguinte sintaxe:

Arquivo do projeto (dbt_project.yml):

models:
  <resource-path>:
    +materialized: view

Ou bloco de configuração (models/<model_name>.sql):

{{ config(materialized = "view") }}

Materialização: tabela

Um modelo do dbt pode ser criado como uma tabela no ClickHouse e configurado com a seguinte sintaxe:

Arquivo do projeto (dbt_project.yml):

models:
  <resource-path>:
    +materialized: table
    +order_by: [ <column-name>, ... ]
    +engine: <engine-type>
    +partition_by: [ <column-name>, ... ]

Ou no bloco de configuração (models/<model_name>.sql):

{{ config(
    materialized = "table",
    engine = "<engine-type>",
    order_by = [ "<column-name>", ... ],
    partition_by = [ "<column-name>", ... ],
      ...
    ]
) }}

Data skipping indexes

Você pode adicionar data skipping indexes às materializações table usando a configuração indexes:

{{ config(
        materialized='table',
        indexes=[{
          'name': 'your_index_name',
          'definition': 'your_column TYPE minmax GRANULARITY 2'
        }]
) }}

Projeções

Você pode adicionar projeções às materializações table e distributed_table por meio da configuração projections. Cada entrada de projeção exige uma chave query ou index (não ambas).

Observação: Em tabelas distribuídas, a projeção é aplicada às tabelas _local, não à tabela proxy distribuída. Observação: Especificar query e index na mesma entrada de projeção gera um erro em tempo de compilação.

Projeções de consulta

Use query para definir uma consulta completa de projeção:

{{ config(
       materialized='table',
       projections=[
           {
               'name': 'your_projection_name',
               'query': 'SELECT department, avg(age) AS avg_age GROUP BY department'
           }
       ]
) }}

Projeções de índice

Use index como uma abreviação sintática para projeções de índice leves que usam a coluna virtual _part_offset. Passe o nome de uma única coluna ou uma lista de colunas para ordenar:

{{ config(
       materialized='table',
       projections=[
           {
               'name': 'proj_by_age',
               'index': 'age'
           }
       ]
) }}
{{ config(
       materialized='table',
       projections=[
           {
               'name': 'proj_by_dept_age',
               'index': ['department', 'age']
           }
       ]
) }}

dbt-clickhouse gera automaticamente DDL adequada à versão:

Versão do ClickHouse SQL gerado
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)

Materialização: incremental

O modelo de tabela será reconstruído a cada execução do dbt. Isso pode ser inviável e extremamente custoso para conjuntos de resultados maiores ou transformações complexas. Para enfrentar esse desafio e reduzir o tempo de compilação, um modelo do dbt pode ser criado como uma tabela incremental do ClickHouse e configurado com a seguinte sintaxe:

Definição do modelo em 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 ]

Ou o bloco de configuração em 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 ],
      ...
    ]
) }}

Configurações

As configurações específicas deste tipo de materialização estão listadas abaixo:

Option Description Required?
unique_key Uma tupla de nomes de colunas que identificam exclusivamente as linhas. Para mais detalhes sobre restrições de unicidade, veja aqui. Obrigatório. Se não for fornecido, linhas alteradas serão adicionadas duas vezes à tabela incremental.
inserts_only Foi descontinuado em favor da strategy incremental append, que funciona da mesma forma. Se definido como True para um modelo incremental, as atualizações incrementais serão inseridas diretamente na tabela de destino sem criar uma tabela intermediária. Se inserts_only for definido, incremental_strategy será ignorado. Opcional (padrão: False)
incremental_strategy A estratégia a ser usada para a materialização incremental. delete+insert, append, insert_overwrite ou microbatch são compatíveis. Para mais detalhes sobre as estratégias, veja aqui Opcional (padrão: 'default')
incremental_predicates Condições adicionais a serem aplicadas à materialização incremental (aplicadas apenas à estratégia delete+insert Opcional

Estratégias de modelos incrementais

dbt-clickhouse oferece suporte a três estratégias de modelos incrementais.

A Estratégia Padrão (Legada)

Historicamente, o ClickHouse oferecia apenas suporte limitado a atualizações e exclusões, na forma de "mutações" assíncronas. Para emular o comportamento esperado do dbt, o dbt-clickhouse, por padrão, cria uma nova tabela temporária contendo todos os registros "antigos" não afetados (não excluídos, não alterados), além de quaisquer registros novos ou atualizados, e então troca essa tabela temporária pela relação incremental existente do modelo. Esta é a única estratégia que preserva a relação original caso algo dê errado antes da conclusão da operação; no entanto, como envolve uma cópia completa da tabela original, sua execução pode ser bastante cara e lenta.

A estratégia Delete+Insert

A estratégia delete+insert usa exclusões leves para remover as linhas afetadas e inserir as novas. Como não copia a tabela inteira, apresenta um desempenho significativamente melhor que a estratégia "legado". Definir use_lw_deletes: true no perfil torna delete+insert a estratégia incremental padrão.

Há ressalvas importantes ao usar essa estratégia:

  • Ela opera diretamente na tabela afetada, sem criar tabelas intermediárias ou temporárias. Portanto, se ocorrer um problema durante a operação, os dados no modelo incremental provavelmente ficarão em um estado inválido.
  • Ela exige a configuração allow_nondeterministic_mutations do ClickHouse. O adaptador a habilita automaticamente em suas próprias sessões, quando possível. Quando não é possível habilitá-la (por exemplo, se ela for somente leitura para seu usuário do dbt), o comportamento depende de como a estratégia foi escolhida: os modelos que usam a estratégia padrão recorrem silenciosamente à estratégia legado; os modelos que definem explicitamente delete+insert ou microbatch falham em tempo de execução; e use_lw_deletes: true no perfil falha no momento da conexão.
  • Em casos muito raros, o uso de incremental_predicates não determinísticos pode resultar em uma condição de corrida nos itens atualizados/excluídos. Para garantir resultados consistentes, os predicados incrementais devem incluir apenas subconsultas sobre dados que não serão modificados durante a materialização incremental.

A estratégia microbatch (requer dbt-core >= 1.9)

A estratégia incremental microbatch é um recurso do dbt-core desde a versão 1.9, projetado para lidar com transformações de grandes volumes de dados de séries temporais com eficiência. No dbt-clickhouse, ela se baseia na estratégia incremental delete_insert já existente, dividindo o processamento incremental em lotes predefinidos de séries temporais com base nas configurações do modelo event_time e batch_size.

Além de lidar com grandes transformações, o microbatch oferece a capacidade de:

Para mais detalhes sobre o uso de microbatch, consulte a documentação oficial.

Configurações de Microbatch disponíveis
Opção Descrição Padrão, se houver
event_time A coluna que indica "em que momento a linha ocorreu". Obrigatória para seu modelo de microbatch e quaisquer modelos pai diretos que devam ser filtrados.
begin O "início dos tempos" do modelo de microbatch. Este é o ponto de partida para quaisquer compilações iniciais ou atualizações completas. Por exemplo, um modelo de microbatch com granularidade diária executado em 2024-10-01 com begin = '2023-10-01 processará 366 batches (é um ano bissexto!) mais o batch de "hoje".
batch_size A granularidade dos seus batches. Os valores compatíveis são hour, day, month e year
lookback Processa X batches antes do marcador mais recente para capturar registros que chegam com atraso. 1
concurrent_batches Substitui a detecção automática do dbt para executar batches de forma concorrente (ao mesmo tempo). Leia mais sobre como configurar batches concorrentes. Definir como true executa os batches de forma concorrente (em paralelo). false executa os batches de forma sequencial (um após o outro).

A estratégia Append

Essa estratégia substitui a configuração inserts_only nas versões anteriores do dbt-clickhouse. Essa abordagem simplesmente adiciona novas linhas à relação existente. Como resultado, linhas duplicadas não são eliminadas, e não há tabela temporária nem intermediária. É a abordagem mais rápida se duplicatas forem permitidas nos dados ou excluídas pela consulta incremental na cláusula WHERE/filtro.

A estratégia insert_overwrite (Experimental)

[IMPORTANT] Atualmente, a estratégia insert_overwrite não é totalmente funcional com materializações distribuídas.

Executa as seguintes etapas:

  1. Cria uma tabela de staging (temporária) com a mesma estrutura da relação do modelo incremental: CREATE TABLE <staging> AS <target>.
  2. Insere apenas novos registros (produzidos por SELECT) na tabela de staging.
  3. Substitui apenas as novas partições (presentes na tabela de staging) na tabela de destino.

Essa abordagem tem as seguintes vantagens:

  • É mais rápida do que a estratégia padrão porque não copia a tabela inteira.
  • É mais segura do que outras estratégias porque não modifica a tabela original até que a operação INSERT seja concluída com sucesso: em caso de falha intermediária, a tabela original não é modificada.
  • Implementa a prática recomendada de engenharia de dados de "imutabilidade de partições", o que simplifica o processamento de dados incremental e paralelo, rollbacks etc.

A estratégia exige que partition_by seja definido na configuração do modelo. Ela ignora todos os demais parâmetros específicos de estratégia na configuração do modelo.

Materialização: materialized_view

A materialização materialized_view cria uma visão materializada no ClickHouse que atua como um gatilho de inserção, transformando e inserindo automaticamente novas linhas de uma tabela de origem em uma tabela de destino. Esta é uma das materializações mais poderosas disponíveis no dbt-clickhouse.

Devido ao nível de detalhamento, esta materialização tem uma página dedicada. Acesse o guia de visões materializadas para consultar a documentação completa

Materialização: dicionário (experimental)

Um modelo dbt pode ser criado como um dicionário do ClickHouse. Em cada dbt run, o dicionário é substituído pela definição atual do modelo usando CREATE OR REPLACE DICTIONARY.

Configurações

Opção Descrição Obrigatório
fields A estrutura do dicionário, como uma lista de pares (name, type). Sim
primary_key A chave primária do dicionário. Deve corresponder ao tipo de chave esperado pelo layout escolhido (por exemplo, uma chave complexa para layouts COMPLEX_KEY_*). Sim
layout O layout usado para armazenar o dicionário na memória, como HASHED(), COMPLEX_KEY_HASHED() ou DIRECT(). Sim
source_type A origem dos dados lidos pelo dicionário: clickhouse (padrão; usa o SQL do modelo ou a opção table) ou http.
lifetime A cláusula LIFETIME que controla a frequência de atualização do dicionário, por exemplo, MIN 0 MAX 300. Opcional desde o dbt-clickhouse 1.10.0 — omita-a para layouts que não a utilizam, como DIRECT().
table Apenas para a origem clickhouse. Lê dados de uma tabela existente em vez do SQL do modelo.
update_field Apenas para a origem clickhouse. Atualiza o dicionário incrementalmente, buscando apenas linhas cujo valor nesta coluna foi alterado desde a atualização anterior. Consulte LIFETIME. Disponível desde o dbt-clickhouse 1.10.0.
update_lag Apenas para a origem clickhouse. Número de segundos subtraídos do horário da atualização anterior ao usar update_field, para considerar atualizações recebidas com atraso. Disponível desde o dbt-clickhouse 1.10.0.
connection_overrides Apenas para a origem clickhouse. Substituições das credenciais usadas na cláusula SOURCE do dicionário, por exemplo, {'user': 'dictionary_reader'}.
url, format Apenas para a origem http. A URL do arquivo de origem e seu formato de entrada. Sim para http
range A cláusula RANGE para layouts RANGE_HASHED(), por exemplo, 'min start max stop'.

Exemplo com uma fonte do ClickHouse

O SQL do modelo se torna a consulta da fonte do dicionário:

{{ 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') }}

Exemplo com uma fonte HTTP

Ao usar source_type='http' (ou a opção table), o SQL do modelo não é usado como fonte, mas o dbt ainda exige um corpo — use select 1 como placeholder:

{{ 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

Consulte os testes de dicionário para mais exemplos, incluindo dicionários range e direct.

Materialização: distributed_table (experimental)

A tabela distribuída é criada nas seguintes etapas:

  1. Cria uma view temporária com uma consulta SQL para obter a estrutura correta
  2. Cria tabelas locais vazias com base na view
  3. Cria uma tabela distribuída com base nas tabelas locais.
  4. Os dados são inseridos na tabela distribuída para que sejam distribuídos entre os shards sem duplicação.

Observações:

  • As consultas do dbt-clickhouse agora incluem automaticamente a configuração insert_distributed_sync = 1 para garantir que operações subsequentes de materialização incremental sejam executadas corretamente. Isso pode fazer com que algumas inserções em tabelas distribuídas sejam executadas mais lentamente do que o esperado.

Exemplo de modelo de tabela distribuída

{{
    config(
        materialized='distributed_table',
        order_by='id, created_at',
        sharding_key='cityHash64(id)',
        engine='ReplacingMergeTree'
    )
}}

select id, created_at, item
from {{ source('db', 'table') }}

Migrações geradas

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));

Configurações

As configurações específicas para este tipo de materialização estão listadas abaixo:

Opção Descrição Padrão, se houver
sharding_key A chave de sharding determina o servidor de destino ao inserir em uma tabela com mecanismo Distributed. A chave de sharding pode ser aleatória ou ser a saída de uma função hash rand())

materialização: distributed_incremental (experimental)

Modelo incremental baseado no mesmo conceito de tabela distribuída; a principal dificuldade é processar corretamente todas as estratégias incrementais.

  1. A estratégia Append simplesmente insere dados na tabela distribuída.
  2. A estratégia Delete+Insert cria uma tabela temporária distribuída para trabalhar com todos os dados em cada shard.
  3. A estratégia padrão (legada) cria tabelas temporárias e intermediárias distribuídas pelo mesmo motivo.

Somente as tabelas de shard são substituídas, porque a tabela distribuída não armazena dados. A tabela distribuída é recarregada apenas quando o modo full_refresh está habilitado ou quando a estrutura da tabela pode ter sido alterada.

Exemplo de modelo incremental distribuído

{{
    config(
        materialized='distributed_incremental',
        engine='MergeTree',
        incremental_strategy='append',
        unique_key='id,created_at'
    )
}}

select id, created_at, item
from {{ source('db', 'table') }}

Migrações geradas

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));

Snapshot

Os snapshots do dbt permitem manter um registro das alterações em um modelo mutável ao longo do tempo. Isso, por sua vez, permite consultas pontuais em modelos, nas quais os analistas podem "voltar no tempo" para ver o estado anterior de um modelo. Essa funcionalidade é compatível com o ClickHouse Connector e é configurada usando a seguinte sintaxe:

Bloco de configuração em snapshots/<model_name>.sql:

{{
   config(
     schema = "<schema-name>",
     unique_key = "<column-name>",
     strategy = "<strategy>",
     updated_at = "<updated-at-column-name>",
   )
}}

Para mais informações sobre configuração, consulte a página de referência configurações de snapshot.

Contratos e restrições

Apenas contratos com correspondência exata de tipo de coluna são compatíveis. Por exemplo, um contrato com uma coluna do tipo UInt32 falhará se o modelo retornar um UInt64 ou outro tipo inteiro. O ClickHouse também oferece suporte apenas a restrições CHECK na tabela/modelo como um todo. Chave primária, chave estrangeira, UNIQUE e restrições CHECK em nível de coluna não são compatíveis. (Consulte a documentação do ClickHouse sobre chaves primárias/chave ORDER BY.)

Navigation