- Criar um projeto dbt e configurar o adaptador do ClickHouse.
- Definir um modelo.
- Atualizar um modelo.
- Criar um modelo incremental.
- Criar um modelo de snapshot.
- Usar visões materializadas.
Configuração
Prepare o ClickHouse
A coluna
created_at da tabela roles, que tem now() como valor padrão. Vamos usá-la mais tarde para identificar atualizações incrementais nos nossos modelos — consulte Modelos Incrementais.s3 para ler os dados de origem a partir de endpoints públicos e inserir os dados. Execute os comandos a seguir para preencher as tabelas:
Conectando ao ClickHouse
-
Crie um projeto dbt. Neste caso, damos a ele o nome da nossa source
imdb. Quando solicitado, selecioneclickhousecomo banco de dados de origem. -
Entre na pasta do seu projeto com
cd: - Neste ponto, você precisará de um editor de texto de sua preferência. Nos exemplos abaixo, usamos o popular VS Code. Ao abrir o diretório IMDB, você deverá ver uma coleção de arquivos yml e sql:
-
Atualize o arquivo
dbt_project.ymlpara especificar nosso primeiro modelo,actor_summary, e defina o profile comoclickhouse_imdb. -
Em seguida, precisamos fornecer ao dbt os detalhes de conexão da nossa instância do ClickHouse. Adicione o seguinte ao arquivo
~/.dbt/profiles.yml.Observe que será necessário alterar o usuário e a senha. Há outras configurações disponíveis documentadas aqui. -
No diretório IMDB, execute o comando
dbt debugpara confirmar se o dbt consegue se conectar ao ClickHouse.Confirme que a resposta incluiConnection test: [OK connection ok], indicando que a conexão foi bem-sucedida.
Criando uma materialização de view simples
CREATE VIEW AS no ClickHouse. Isso não requer armazenamento adicional de dados, mas as consultas serão mais lentas do que com materializações de tabela.
-
No diretório
imdb, exclua o diretóriomodels/example: -
Crie um novo arquivo em
actors, dentro da pastamodels. Aqui, criamos arquivos, cada um representando um modelo de ator: -
Crie os arquivos
schema.ymleactor_summary.sqlna pastamodels/actors.O arquivoschema.ymldefine nossas tabelas. Depois, elas estarão disponíveis para uso em macros. Editemodels/actors/schema.ymlpara que contenha este conteúdo:Oactors_summary.sqldefine o modelo propriamente dito. Observe que, na função config, também solicitamos que o modelo seja materializado como uma view no ClickHouse. Nossas tabelas são referenciadas no arquivoschema.ymlpor meio da funçãosource; por exemplo,source('imdb', 'movies')refere-se à tabelamoviesno banco de dadosimdb. Editemodels/actors/actors_summary.sqlpara que contenha este conteúdo:Observe que incluímos a colunaupdated_atno nosso actor_summary final. Usamos isso mais tarde em materializações incrementais. -
No diretório
imdb, execute o comandodbt run. -
O dbt representará o model como uma view no ClickHouse, conforme solicitado. Agora podemos consultar essa view diretamente. Essa view terá sido criada no banco de dados
imdb_dbt— isso é determinado pelo parâmetro schema no arquivo~/.dbt/profiles.yml, no perfilclickhouse_imdb.Ao consultar esta view, podemos reproduzir os resultados da nossa consulta anterior com uma sintaxe mais simples:
Criando uma materialização como tabela
SELECT mais complexas ou consultas executadas com frequência podem ter melhor desempenho quando materializadas como tabela. Essa materialização é útil para modelos que serão consultados por ferramentas de BI, garantindo uma experiência mais rápida para os usuários. Na prática, isso faz com que os resultados da consulta sejam armazenados em uma nova tabela, com a sobrecarga de armazenamento correspondente — ou seja, um INSERT TO SELECT é executado. Observe que essa tabela será reconstruída todas as vezes, ou seja, não é incremental. Portanto, grandes conjuntos de resultados podem levar a tempos de execução longos — consulte Limitações do dbt.
-
Modifique o arquivo
actors_summary.sqlpara que o parâmetromaterializedseja definido comotable. Observe comoORDER BYé definido e note que usamos o mecanismo de tabelaMergeTree: -
No diretório
imdb, execute o comandodbt run. Essa execução pode levar um pouco mais de tempo — cerca de 10s na maioria das máquinas. -
Confirme a criação da tabela
imdb_dbt.actor_summary:Você deverá ver a tabela com os tipos de dados apropriados: -
Confirme que os resultados desta tabela são consistentes com as respostas anteriores. Observe a melhora perceptível no tempo de resposta agora que o modelo é uma tabela:
Sinta-se à vontade para executar outras consultas nesse modelo. Por exemplo, quais atores têm os filmes com melhor classificação entre aqueles com mais de 5 aparições?
Criando uma materialização incremental
-
Primeiro, modificamos nosso model para que ele seja do tipo incremental. Essa alteração exige:
- unique_key - Para garantir que o adaptador consiga identificar as linhas de forma única, precisamos fornecer uma unique_key — neste caso, o campo
idda nossa consulta será suficiente. Isso garante que não teremos linhas duplicadas na nossa tabela materializada. Para mais detalhes sobre restrições de unicidade, veja aqui. - Filtro incremental - Também precisamos informar ao dbt como ele deve identificar quais linhas foram alteradas em uma execução incremental. Isso é feito fornecendo uma expressão delta. Normalmente, isso envolve um timestamp para dados de evento; por isso, usamos nosso campo de timestamp updated_at. Essa coluna, que por padrão recebe o valor de now() quando as linhas são inseridas, permite identificar novos registros. Além disso, precisamos identificar o caso alternativo em que novos atores são adicionados. Usando a variável
{{this}}para representar a tabela materializada existente, chegamos à expressãowhere id > (select max(id) from {{ this }}) or updated_at > (select max(updated_at) from {{this}}). Incorporamos isso dentro da condição{% if is_incremental() %}, garantindo que ela seja usada apenas em execuções incrementais, e não quando a tabela é criada pela primeira vez. Para mais detalhes sobre a filtragem de linhas em modelos incrementais, veja esta discussão na documentação do dbt.
actor_summary.sqlda seguinte forma:Observe que nosso model responderá apenas a atualizações e adições nas tabelasroleseactors. Para responder a todas as tabelas, recomenda-se dividir este model em vários submodels, cada um com seus próprios critérios incrementais. Esses models, por sua vez, podem ser referenciados e conectados. Para mais detalhes sobre referências cruzadas entre models, veja aqui. - unique_key - Para garantir que o adaptador consiga identificar as linhas de forma única, precisamos fornecer uma unique_key — neste caso, o campo
-
Execute um
dbt rune confirme os resultados na tabela resultante: -
Agora vamos adicionar dados ao nosso modelo para ilustrar uma atualização incremental. Adicione nosso ator “Clicky McClickHouse” à tabela
actors: -
Vamos fazer com que “Clicky” estrele em 910 filmes aleatórios:
-
Confirme que ele agora é, de fato, o ator com mais aparições consultando diretamente a tabela de origem subjacente, sem passar por nenhum modelo do dbt:
-
Execute um
dbt rune confirme que nosso modelo foi atualizado e corresponde aos resultados acima:
Aspectos internos
- O adaptador cria uma tabela temporária
actor_sumary__dbt_tmp. As linhas alteradas são enviadas para essa tabela. - Uma nova tabela,
actor_summary_new,é criada. As linhas da tabela antiga, por sua vez, são enviadas da tabela antiga para a nova, com uma verificação para garantir que os IDs das linhas não existam na tabela temporária. Isso lida de forma eficaz com atualizações e duplicatas. - Os resultados da tabela temporária são enviados para a nova tabela
actor_summary: - Por fim, a nova tabela é trocada atomicamente com a versão antiga por meio de uma instrução
EXCHANGE TABLES. A tabela antiga e a temporária são então removidas.
Estratégia Append (modo apenas inserções)
incremental_strategy. Ele pode ser definido com o valor append. Quando isso é feito, as linhas atualizadas são inseridas diretamente na tabela de destino (também chamada de imdb_dbt.actor_summary) e nenhuma tabela temporária é criada.
Observação: o modo append-only exige que seus dados sejam imutáveis ou que duplicatas sejam aceitáveis. Se você quiser um modelo de tabela incremental com suporte a linhas alteradas, não use este modo!
Para ilustrar este modo, vamos adicionar mais um ator novo e executar novamente o dbt run com incremental_strategy='append'.
-
Configure o modo append-only em actor_summary.sql:
-
Vamos adicionar mais um ator famoso: Danny DeBito
-
Vamos colocar Danny no elenco de 920 filmes aleatórios.
-
Execute um dbt run e confirme que Danny foi adicionado à tabela actor_summary
imdb_dbt.actor_summary, sem envolver a criação de tabela.
Modo de exclusão e inserção (experimental)
incremental_strategy, ou seja.
- O adaptador cria uma tabela temporária
actor_sumary__dbt_tmp. As linhas alteradas são gravadas nessa tabela. - Um
DELETEé executado na tabelaactor_summaryatual. As linhas são excluídas por id com base emactor_sumary__dbt_tmp - As linhas de
actor_sumary__dbt_tmpsão inseridas emactor_summaryusandoINSERT INTO actor_summary SELECT * FROM actor_sumary__dbt_tmp.
modo insert_overwrite (experimental)
- Crie uma tabela de staging (temporária) com a mesma estrutura da relação do modelo incremental:
CREATE TABLE {staging} AS {target}. - Insira apenas os novos registros (produzidos por SELECT) na tabela de staging.
- Substitua 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 das partições”, o que simplifica o processamento de dados incremental e paralelo, rollbacks etc.
Criando um snapshot
-
Crie um arquivo
actor_summaryno diretório snapshots. -
Atualize o conteúdo do arquivo actor_summary.sql com o conteúdo a seguir:
- A consulta
selectdefine os resultados que você deseja capturar em snapshots ao longo do tempo. A função ref é usada para referenciar o modelo actor_summary que criamos anteriormente. - Precisamos de uma coluna de timestamp para indicar alterações nos registros. Nossa coluna updated_at (consulte Criando um modelo de tabela incremental) pode ser usada aqui. O parâmetro strategy indica que usamos um timestamp para marcar atualizações, e o parâmetro updated_at especifica qual coluna usar. Se ele não estiver presente no seu modelo, você também pode usar a estratégia check. Isso é significativamente menos eficiente e exige que o usuário especifique uma lista de colunas para comparação. O dbt compara os valores atuais e históricos dessas colunas, registrando quaisquer alterações (ou não fazendo nada se forem idênticos).
-
Execute o comando
dbt snapshot.
-
Ao examinar uma amostra desses dados, você verá como o dbt incluiu as colunas dbt_valid_from e dbt_valid_to. Esta última tem valores definidos como NULL. Nas execuções seguintes, isso será atualizado.
-
Faça nosso ator favorito, Clicky McClickHouse, aparecer em outros 10 filmes.
-
Execute novamente o comando
dbt runa partir do diretórioimdb. Isso atualizará o modelo incremental. Quando isso for concluído, execute odbt snapshotpara capturar as alterações. -
Se agora consultarmos nosso snapshot, observe que temos 2 linhas para Clicky McClickHouse. Nossa entrada anterior agora tem um valor em dbt_valid_to. Nosso novo valor é registrado com o mesmo valor na coluna dbt_valid_from e com dbt_valid_to igual a null. Se tivéssemos novas linhas, elas também seriam adicionadas ao snapshot.
Usando seeds
-
Geramos uma lista de códigos de gênero a partir do nosso conjunto de dados existente. No diretório do dbt, use o
clickhouse-clientpara criar um arquivoseeds/genre_codes.csv: -
Execute o comando
dbt seed. Isso criará uma nova tabelagenre_codesno nosso banco de dadosimdb_dbt(conforme definido na nossa configuração de schema) com as linhas do nosso arquivo CSV. -
Confirme que os dados foram carregados: