materialized_view deve ser um SELECT a partir de uma tabela de origem existente. Diferentemente do PostgreSQL, uma visão materializada no ClickHouse não é “estática” (e não tem uma operação REFRESH correspondente). Em vez disso, ela atua como um gatilho de inserção, inserindo novas linhas em uma tabela de destino ao aplicar a transformação SELECT definida às linhas inseridas na tabela de origem. Consulte a documentação sobre visões materializadas no ClickHouse para mais detalhes sobre como as visões materializadas funcionam no ClickHouse.
Para conceitos gerais de materialização e configurações compartilhadas (engine, order_by, partition_by etc.), consulte a página Materializações.
Como a tabela de destino é gerenciada
materialized_view, o dbt-clickhouse precisa criar tanto uma visão materializada quanto uma tabela de destino na qual as linhas transformadas são inseridas. Há duas formas de gerenciar a tabela de destino:
A abordagem escolhida afeta como mudanças de esquema, full refreshes e configurações com múltiplas MVs são tratadas. As seções a seguir descrevem cada abordagem em detalhes.
Materialização com destino implícito
materialized_view, o adaptador irá:
- Criar uma tabela de destino com o nome do modelo
- Criar uma visão materializada no ClickHouse com o nome
<model_name>_mv
SELECT da MV. Todos os recursos (tabela de destino + MVs) compartilham a mesma configuração do modelo.
Múltiplas visões materializadas
UNION no arquivo do modelo, envolvendo o SQL de cada visão materializada com comentários no formato --my_mv_name:begin e --my_mv_name:end.
Por exemplo, o seguinte criará duas visões materializadas, ambas gravando dados na mesma tabela de destino do modelo. Os nomes das visões materializadas terão o formato <model_name>_mv1 e <model_name>_mv2:
Como iterar o esquema da tabela de destino
dbt run encontra colunas diferentes no SQL da MV.
ignore), mas você pode alterar essa configuração para seguir o mesmo comportamento da configuração on_schema_change em modelos incrementais.
Além disso, você pode usar essa configuração como um mecanismo de proteção. Se defini-la como fail, a compilação falhará se as colunas no SQL da MV forem diferentes das da tabela de destino criada na primeira execução de dbt run.
Carga retroativa de dados
catchup=True). Você pode desativar esse comportamento definindo a configuração catchup como False.
Materialização com destino explícito (Beta)
- Todos os recursos (tabela de destino + MVs) compartilham a mesma configuração. Se várias MVs apontarem para a mesma tabela de destino, elas deverão ser definidas em conjunto usando a sintaxe
UNION ALL. - Nenhum desses recursos pode ser tratado separadamente; todos precisam ser gerenciados usando o mesmo arquivo de modelo.
- Você não consegue controlar facilmente o nome de cada MV.
- Todas as configurações são compartilhadas entre a tabela de destino e as MVs, o que dificulta configurar cada recurso individualmente e entender qual configuração pertence a cada um.
table comum e, em seguida, referenciá-la a partir dos seus modelos de visão materializada.
Benefícios
- Recursos totalmente separados: Agora cada recurso pode ser definido separadamente, melhorando a legibilidade.
- Recursos 1:1 entre dbt e CH: Agora você pode usar as ferramentas do dbt para gerenciá-los e iterar sobre eles separadamente.
- Diferentes configurações agora disponíveis: Agora é possível aplicar uma configuração diferente a cada um deles.
- Não é mais necessário seguir convenções de nomenclatura: Agora todos os recursos são criados com o nome que você definir, e não com o nome personalizado adicionado com _mv para as MVs.
Limitações
- A definição da tabela de destino não é algo natural no dbt: não é um SQL que vai ler de uma tabela de origem, então você perde as validações do dbt nesse ponto. O SQL da MV ainda será validado usando os utilitários do dbt, e a compatibilidade dele com as colunas da tabela de destino será validada no nível do CH.
- Encontramos alguns problemas relacionados às limitações da função
ref(): precisamos usá-la para referenciar modelos entre si, mas ela só pode ser usada para referenciar modelos upstream, não downstream. Isso traz alguns problemas para esta implementação. Criamos uma issue no repositório dbt-core e, no momento, estamos conversando com eles para buscar possíveis soluções (dbt-labs/dbt-core#12319):- Quando
ref()é chamada de dentro do bloco de configuração, ela retorna o modelo atual, e não o modelo compartilhado. Isso nos impede de defini-la na seção config(), obrigando-nos a usar um comentário para adicionar essa dependência. Estamos seguindo o mesmo padrão definido na documentação do dbt com a abordagem “—depends_on:”. ref()funciona no nosso caso, pois força a criação da tabela de destino primeiro, mas, no gráfico de dependências da documentação gerada, a tabela de destino aparecerá como mais uma dependência upstream, e não downstream, o que dificulta um pouco o entendimento.unit-testtambém nos obriga a definir alguns dados para a tabela de destino, mesmo quando a ideia não é ler dela. A solução alternativa é simplesmente deixar vazios os dados dessa tabela.
- Quando
Uso
events_daily.sql:
{{ materialization_target_table(ref('events_daily')) }}, que configura a tabela de destino da MV.
Modelo page_events_aggregator.sql:
mobile_events_aggregator.sql:
Opções de configuração
materialized='table'):
Na visão materializada (
materialized='materialized_view'):
Em geral, convém definir apenas
catchup como True nas MVs ou repopulate_from_mvs_on_full_refresh como True nas tabelas de destino correspondentes. Se você definir ambos como True, isso pode duplicar dados.Operações comuns
Atualização completa com tabelas de destino explícitas
--full-refresh, as tabelas de destino explícitas serão recriadas (portanto, você poderá perder dados se a ingestão estiver em andamento durante esse processo). O comportamento varia de acordo com a sua configuração:
Opção 1: comportamento padrão do --full-refresh. Tudo é recriado, mas, durante a recriação das MVs, a tabela de destino ficará vazia ou parcialmente carregada.
Tudo é removido e recriado. Se você quiser reinserir os dados usando o SQL das MVs, mantenha a configuração catchup=True:
catchup=False nelas e depois executar um dbt run ou dbt run --full-refresh nas MVs. Certifique-se de que as MVs sejam criadas antes de executar --full-refresh na tabela de destino, pois isso usa as definições das MVs do ClickHouse.
Defina repopulate_from_mvs_on_full_refresh=True no modelo da tabela de destino. Em um dbt run --full-refresh, isso irá:
- Criar uma nova tabela temporária
- Executar INSERT-SELECT usando o SQL de cada MV
- Trocar as tabelas atomicamente
Alterando a tabela de destino
--full-refresh. Se tentar executar um dbt run normal após alterar a referência materialization_target_table(), a compilação falhará com uma mensagem de erro indicando que o destino foi alterado.
Para alterar o destino:
- Atualize a chamada
materialization_target_table() - Execute
dbt run --full-refresh -s your_mv_model
Solução de problemas comuns
A tabela de destino fica vazia durante/após a execução de run
- As visões materializadas podem estar configuradas com
catchup=Falseou a tabela de destino pode estar configurada comrepopulate_from_mvs_on_full_refresh=False, portanto nenhuma carga retroativa é executada quando as visões materializadas são criadas ou quando a tabela de destino é recriada. Esse é o comportamento esperado. Portanto, se você quiser reinserir os dados usando o SQL das visões materializadas, definacatchup=Truena visão materializada (esse é o valor padrão) ourepopulate_from_mvs_on_full_refresh=Truena tabela de destino. Certifique-se de não ativar os dois ao mesmo tempo para evitar duplicatas. Consulte a seção de configuração para mais detalhes. - Durante a execução de
dbt run --full-refresh, se as visões materializadas usarem o padrãocatchup=True, o destino será recriado e as MVs reinserirão os dados sequencialmente. Para evitar essa situação, consulte Atualização completa com destinos explícitos.
dbt run --full-refresh em uma tabela de destino com repopulate_from_mvs_on_full_refresh=True usa a lógica de versões antigas de visões materializadas, e não o SQL que está atualmente no projeto
repopulate_from_mvs_on_full_refresh=True usa o SQL existente da MV já definido no ClickHouse. Para garantir que a nova definição da visão materializada seja usada, execute um dbt run para cada visão materializada antes de executar dbt run --full-refresh na tabela de destino.
Há dados duplicados após a execução de um run
- Tanto
catchup=Truenas visões materializadas quantorepopulate_from_mvs_on_full_refresh=Truena tabela de destino podem estar habilitados: mantenha apenas um deles, dependendo das operações que pretende executar. Consulte a seção de configuração para mais detalhes. - A tabela de destino não foi definida com
WHERE 0: a tabela de destino deve ser criada vazia, mas a consulta interna pode inserir dados seWHERE 0não for incluído. Certifique-se de que a cláusula esteja incluída.
Perda de dados durante a ingestão ativa após a execução de um dbt run --full-refresh
dbt run --full-refresh.
As visões materializadas do ClickHouse funcionam como gatilhos de inserção — elas só capturam dados enquanto existem. Durante um full refresh, há uma breve janela em que a MV é removida e recriada (a “janela cega”). Quaisquer linhas inseridas na tabela de origem durante essa janela não são capturadas. Consulte a seção Comportamento durante a ingestão ativa para mais detalhes.
Técnicas de depuração
Verifique o destino atual de uma MV no ClickHouse
system.tables para ver para onde uma visão materializada está gravando:
Verifique se o dbt reconhece uma tabela como tabela de destino de uma visão materializada
A tabelaSe esta mensagem aparecer, o dbt detectou que a tabela é a tabela de destino de pelo menos uma visão materializada gerenciada pelo dbt. Se você espera ver essa mensagem, mas ela não aparecer, verifique se:<table_name>está sendo usada como tabela de destino por uma visão materializada gerenciada pelo dbt. Definindomv_on_schema_changecomo “fail” por padrão para evitar perda de dados.
- O modelo da visão materializada define
{{ materialization_target_table(ref('your_target')) }}corretamente - O modelo da visão materializada tem
materialized='materialized_view'na configuração - Tanto a visão materializada quanto a tabela de destino foram executadas pelo menos uma vez
Migrando de destino implícito para destino explícito
materialized='table' que defina o mesmo esquema da tabela de destino da MV atual. Use uma cláusula WHERE 0 para criar uma tabela vazia. Use o mesmo nome do modelo atual de visão materializada implícita. A partir de agora, você poderá usar esse modelo para fazer iterações na tabela de destino.
materialization_target_table() apontando para a nova tabela de destino. Se você estava usando UNION ALL, remova essa parte e os comentários.
Para os nomes dos modelos, você terá que seguir esta convenção de nomenclatura:
- se apenas uma MV foi definida, ela terá o nome:
<old_model_name>_mv - se várias MVs foram definidas, cada uma terá o nome:
<old_model_name>_mv_<name_in_comments>
my_model.sql (tabela de destino implícita, modelo único com UNION ALL):
Comparação de comportamento entre as abordagens de destino implícito e destino explícito
Como se comportam em geral
Comportamento durante a ingestão ativa
- Como as visões materializadas do ClickHouse atuam como gatilhos de inserção, elas só capturam dados enquanto existem. Se uma visão materializada for removida e recriada (por exemplo, durante um
--full-refresh), quaisquer linhas inseridas na tabela de origem durante esse intervalo não serão processadas pela visão materializada. Isso é chamado de visão materializada “cega”. - Os diferentes processos de
catchupse baseiam em operaçõesINSERT INTO ... SELECTque usam o SQL das visões materializadas e são independentes do funcionamento delas. Depois que oINSERTcomeça, os novos dados não são capturados por ele, mas serão capturados pela visão materializada anexada.
Operações de destino implícito
Operações com destino explícito
Modelo de tabela de destino:
Visões Materializadas Atualizáveis
refreshable ao seu modelo de MV com as seguintes opções:
Exemplo com destino implícito
Exemplo com destino explícito
Limitações
- Ao criar uma visão materializada atualizável (MV) no ClickHouse que tenha uma dependência, o ClickHouse não gera um erro se a dependência especificada não existir no momento da criação. Em vez disso, a MV atualizável permanece em estado inativo, aguardando que a dependência seja atendida antes de começar a processar atualizações ou ser atualizada. Esse comportamento é intencional, mas pode causar atrasos na disponibilidade dos dados se a dependência necessária não for resolvida rapidamente. Garanta que todas as dependências estejam corretamente definidas e existam antes de criar uma visão materializada atualizável.
- Até o momento, não há um “vínculo com o dbt” real entre a MV e suas dependências; portanto, a ordem de criação não é garantida.
- O recurso de atualização não foi testado com várias MVs apontando para o mesmo modelo de destino.