materialized_view debe ser un SELECT sobre una tabla de origen existente. A diferencia de PostgreSQL, una vista materializada de ClickHouse no es “estática” (y no tiene una operación REFRESH equivalente). En cambio, actúa como un disparador de inserción: inserta nuevas filas en una tabla de destino aplicando la transformación SELECT definida a las filas que se insertan en la tabla de origen. Consulta la documentación sobre vistas materializadas de ClickHouse para obtener más información sobre cómo funcionan las vistas materializadas en ClickHouse.
Para consultar los conceptos generales de materialización y las configuraciones compartidas (engine, order_by, partition_by, etc.), consulta la página Materializaciones.
Cómo se gestiona la tabla de destino
materialized_view, dbt-clickhouse necesita crear tanto una vista materializada como una tabla de destino donde se insertan las filas transformadas. Hay dos formas de gestionar la tabla de destino:
El enfoque que elijas afecta a cómo se gestionan los cambios de esquema, los
full refresh y las configuraciones con varias MV. Las siguientes secciones describen cada enfoque en detalle.
Materialización con tabla de destino implícita
materialized_view, el adaptador hará lo siguiente:
- Crear una tabla de destino con el nombre del modelo
- Crear una vista materializada de ClickHouse con el nombre
<model_name>_mv
SELECT de la vista materializada. Todos los recursos (tabla de destino + MVs) comparten la misma configuración del modelo.
Múltiples vistas materializadas
UNION en tu archivo de modelo, delimitando el SQL de cada vista materializada con comentarios del tipo --my_mv_name:begin y --my_mv_name:end.
Por ejemplo, lo siguiente creará dos vistas materializadas, ambas escribiendo datos en la misma tabla de destino del modelo. Los nombres de las vistas materializadas tendrán la forma <model_name>_mv1 y <model_name>_mv2:
Cómo actualizar el esquema de la tabla de destino
dbt run encuentra columnas distintas en el SQL de la MV.
ignore), pero puede cambiar esta configuración para que siga el mismo comportamiento que la configuración on_schema_change en los modelos incrementales.
Además, puede usar esta configuración como mecanismo de seguridad. Si la establece en fail, la compilación fallará si las columnas de la consulta SQL de la MV difieren de las de la tabla de destino que se creó con el primer dbt run.
Puesta al día de los datos
catchup=True). Puede desactivar este comportamiento estableciendo la configuración catchup en False.
Materialización con destino explícito (Beta)
- Todos los recursos (tabla de destino + MVs) comparten la misma configuración. Si varias MVs apuntan a la misma tabla de destino, deben definirse juntas mediante la sintaxis
UNION ALL. - Ninguno de estos recursos puede procesarse por separado; todos deben administrarse mediante el mismo archivo de modelo.
- No es fácil controlar el nombre de cada MV.
- Toda la configuración se comparte entre la tabla de destino y las MVs, lo que dificulta configurar cada recurso individualmente y determinar qué configuración corresponde a cada uno.
table normal y luego hacer referencia a ella desde tus modelos de vista materializada.
Beneficios
- Recursos totalmente separados: Ahora cada recurso puede definirse por separado, lo que mejora la legibilidad.
- Recursos 1:1 entre dbt y CH: Ahora puedes usar las herramientas de dbt para gestionarlos y modificarlos por separado.
- Ahora hay distintas configuraciones disponibles: Ahora se puede aplicar una configuración diferente a cada uno.
- Ya no es necesario mantener convenciones de nomenclatura: Ahora todos los recursos se crean con el nombre que les des, no con el nombre personalizado añadido con _mv para las MV.
Limitaciones
- La definición de la tabla de destino no es natural en dbt: no es un SQL que lea de una tabla de origen, por lo que aquí se pierden las validaciones de dbt. El SQL de la MV se seguirá validando mediante las utilidades de dbt, y su compatibilidad con las columnas de la tabla de destino se validará a nivel de CH.
- Hemos encontrado algunos problemas relacionados con las limitaciones de la función
ref(): Necesitamos usarla para referenciar modelos entre sí, pero solo puede usarse para referenciar modelos upstream, no downstream. Esto genera algunos problemas en esta implementación. Hemos creado una incidencia en el repositorio de dbt-core y actualmente estamos hablando con ellos para buscar posibles soluciones (dbt-labs/dbt-core#12319):- Cuando se llama a
ref()desde dentro del bloque de configuración, devuelve el modelo actual, no el compartido. Esto nos impide definirlo en la secciónconfig(), por lo que nos obliga a usar un comentario para añadir esta dependencia. Estamos siguiendo el mismo patrón definido en la documentación de dbt con el enfoque “—depends_on:”. ref()nos funciona porque fuerza que la tabla de destino se cree primero, pero en el gráfico de dependencias de la documentación generada, la tabla de destino aparecerá como otra dependencia upstream, no downstream, lo que hace que sea algo difícil de entender.unit-testtambién nos obliga a definir algunos datos para la tabla de destino, incluso cuando la idea no es leer de ella. La solución alternativa es simplemente dejar vacíos los datos de esta tabla.
- Cuando se llama a
Uso
events_daily.sql:
{{ materialization_target_table(ref('events_daily')) }}, que configura la tabla de destino de la MV.
Modelo page_events_aggregator.sql:
mobile_events_aggregator.sql:
Opciones de configuración
materialized='table'):
En la vista materializada (
materialized='materialized_view'):
Normalmente, solo querrás establecer
catchup en True en las MVs o repopulate_from_mvs_on_full_refresh en True en sus tablas de destino. Si estableces ambos en True, es posible que se dupliquen los datos.Operaciones habituales
Actualización completa con tablas de destino explícitas
--full-refresh, las tablas de destino explícitas se volverán a crear (por lo que podrías perder datos si se está ingestando información durante este proceso). Esto se comportará de distintas maneras según la configuración:
Opción 1: comportamiento predeterminado de --full-refresh. Todo se elimina y se vuelve a crear, pero durante la recreación de las MV, la tabla de destino estará vacía o cargada parcialmente.
Todo se elimina y se vuelve a crear. Si quieres volver a insertar los datos usando el SQL de las MV, mantén la configuración catchup=True:
catchup=False y luego ejecutar un dbt run o dbt run --full-refresh sobre las MV. Asegúrate de que las MV se creen antes de ejecutar --full-refresh en la tabla de destino, ya que este usa las definiciones de las MV de ClickHouse.
Establece repopulate_from_mvs_on_full_refresh=True en el modelo de la tabla de destino. En un dbt run --full-refresh, esto hará lo siguiente:
- Crear una nueva tabla temporal
- Ejecutar INSERT-SELECT usando el SQL de cada MV
- Intercambiar atómicamente las tablas
Cambiar la tabla de destino
--full-refresh. Si intentas ejecutar un dbt run normal después de cambiar la referencia de materialization_target_table(), la compilación fallará con un mensaje de error que indica que el destino ha cambiado.
Para cambiar el destino:
- Actualiza la llamada a
materialization_target_table() - Ejecuta
dbt run --full-refresh -s your_mv_model
Solución de problemas comunes
La tabla de destino está vacía mientras/después de ejecutar run
- Las vistas materializadas pueden estar configuradas con
catchup=Falseo la tabla de destino puede estar configurada conrepopulate_from_mvs_on_full_refresh=False, por lo que no se realizará ningún relleno de datos históricos cuando se creen las vistas materializadas o cuando se vuelva a crear la tabla de destino. Este es el comportamiento esperado, así que, si quiere volver a insertar los datos usando el SQL de las vistas materializadas, asegúrese de establecercatchup=Trueen la vista materializada (este es el valor predeterminado) orepopulate_from_mvs_on_full_refresh=Trueen la tabla de destino. Asegúrese de no activar ambas opciones al mismo tiempo para evitar duplicados. Consulte la sección de configuración para obtener más detalles. - Mientras se ejecuta
dbt run --full-refresh, si las vistas materializadas usan el valor predeterminadocatchup=True, el destino se volverá a crear y las MV volverán a insertar los datos de forma secuencial. Para evitar esta situación, consulte Full refresh con destinos explícitos.
dbt run --full-refresh en una tabla de destino con repopulate_from_mvs_on_full_refresh=True usa la lógica de versiones anteriores de la vista materializada, no la del SQL que está actualmente en el proyecto
repopulate_from_mvs_on_full_refresh=True usa el SQL de la MV existente que ya está definida en ClickHouse. Para asegurarte de que se use la nueva definición de la vista materializada, ejecuta dbt run para cada vista materializada antes de ejecutar dbt run --full-refresh en la tabla de destino.
Hay datos duplicados tras ejecutar una ejecución
- Tanto
catchup=Trueen las vistas materializadas comorepopulate_from_mvs_on_full_refresh=Trueen la tabla de destino pueden estar habilitados: mantén solo uno de ellos, según las operaciones que quieras ejecutar. Consulta la sección de configuración para obtener más detalles. - La tabla de destino no está definida con
WHERE 0: la tabla de destino debe crearse vacía, pero la consulta interna puede insertar datos si no se incluyeWHERE 0. Asegúrate de que la cláusula esté incluida.
Pérdida de datos durante la ingestión activa después de ejecutar dbt run --full-refresh
dbt run --full-refresh, faltan algunas filas de la tabla de origen en la tabla de destino.
Las vistas materializadas de ClickHouse actúan como disparadores de inserción: solo capturan datos mientras existen. Durante una actualización completa, hay un breve intervalo en el que la MV se elimina y se vuelve a crear (la “ventana ciega”). Las filas insertadas en la tabla de origen durante este intervalo no se capturan. Consulta la sección Comportamiento durante la ingestión activa para más detalles.
Técnicas de depuración
Verifica el destino actual de una MV en ClickHouse
system.tables para ver dónde escribe una vista materializada:
Comprueba si dbt reconoce una tabla como destino de una vista materializada
La tablaSi aparece este mensaje, dbt ha detectado que la tabla es el destino de al menos una vista materializada gestionada por dbt. Si esperas este mensaje pero no lo ves, verifica que:<table_name>se usa como destino de una vista materializada gestionada por dbt. Se establecemv_on_schema_changeen “fail” de forma predeterminada para evitar la pérdida de datos.
- El modelo de vista materializada define
{{ materialization_target_table(ref('your_target')) }}correctamente - El modelo de vista materializada tiene
materialized='materialized_view'en su configuración - Tanto la vista materializada como la tabla de destino se han ejecutado al menos una vez
Migración de destino implícito a destino explícito
materialized='table' que defina el mismo esquema que la tabla de destino de la MV actual. Use una cláusula WHERE 0 para crear una tabla vacía. Use el mismo nombre que el modelo actual de vista materializada implícita. Ahora podrá usar este modelo para iterar sobre la tabla de destino.
materialization_target_table() que apunte a la nueva tabla de destino. Si antes usabas UNION ALL, elimina esa parte y los comentarios.
Para los nombres de los modelos, tendrás que seguir esta convención:
- si solo se definió una MV, tendrá este nombre:
<old_model_name>_mv - si se definieron varias MV, cada una tendrá este nombre:
<old_model_name>_mv_<name_in_comments>
my_model.sql (destino implícito, modelo único con UNION ALL):
Comparación del comportamiento entre los enfoques de destino implícito y destino explícito
Comportamiento general
Comportamiento durante la ingestión activa
- Como las vistas materializadas de ClickHouse actúan como triggers de inserción, solo capturan datos mientras existen. Si una vista materializada se elimina y se vuelve a crear (p. ej., durante un
--full-refresh), cualquier fila insertada en la tabla de origen durante ese intervalo no será procesada por la vista materializada. A esto se le llama que la vista materializada está “ciega”. - Los distintos procesos de
catchupse basan en operacionesINSERT INTO ... SELECTque usan el SQL de las vistas materializadas y son independientes de cómo funcionan estas. Una vez que comienza elINSERT, los datos nuevos no se capturan por esa operación, pero sí los capturará la vista materializada adjunta.
Operaciones con destino implícito
Operaciones con destino explícito
Modelo de tabla de destino:
Vistas materializadas actualizables
refreshable a tu modelo de MV con las siguientes opciones:
Ejemplo con destino implícito
Ejemplo con destino explícito
Limitaciones
- Al crear una vista materializada actualizable (MV) en ClickHouse que tiene una dependencia, ClickHouse no devuelve un error si la dependencia especificada no existe en el momento de la creación. En su lugar, la MV actualizable permanece en un estado inactivo, a la espera de que se cumpla la dependencia antes de empezar a procesar actualizaciones o a refrescarse. Este comportamiento es intencional, pero puede provocar retrasos en la disponibilidad de los datos si la dependencia requerida no se resuelve con prontitud. Debe asegurarse de que todas las dependencias estén correctamente definidas y existan antes de crear una vista materializada actualizable.
- A día de hoy, no existe una “vinculación con dbt” real entre la mv y sus dependencias, por lo que el orden de creación no está garantizado.
- La funcionalidad de actualización no se ha probado con varias mvs dirigidas al mismo modelo de destino.