Logo Google Cloud con Eduardo

Google Cloud con Eduardo

Descarga el código de la lección

Información básica de protección de datos: Responsable: Eduardo Martínez Agrelo. Finalidad: Gestionar y facilitar la descarga del recurso solicitado. Legitimación: Consentimiento del interesado (Art. 6.1.a RGPD). Destinatarios: Proveedor de infraestructura técnica (Google Cloud / Firebase). Derechos: Acceso, rectificación y supresión en eduardomartinezagrelo@gmail.com.
dbt en BigQuery: Arquitectura Enterprise, Modelado Incremental y Optimización FinOps
✦ Guía Técnica & Arquitectura

dbt en Google BigQuery: Arquitectura Enterprise y FinOps

Domina el modelado de datos modular con dbt sobre BigQuery. Diseña materializaciones incrementales deterministas, particionado granular, clustering selectivo y optimiza drásticamente los costes de cómputo por slot y bytes escaneados en producción. Si prefieres una alternativa nativa de Google Cloud para orquestar el mismo ciclo ELT con SQLX y assertions, revisa la guía de Dataform en BigQuery.

Lo que aprenderás en esta guía

Estrategias de materialización incremental: merge vs. insert_overwrite.
Diseño de particionado por tiempo/entero y clustering de alta selectividad.
Eliminación de escaneos completos (Full Table Scans) y gobierno de slots en BigQuery.
Patrón de contratos de datos, Slim CI/CD y control estricto de costes analíticos.
dbt Core / Cloud Google BigQuery Jinja Templating SQL Modeling FinOps Data Architecture CI/CD Slim Runs

Estrategias de Materialización en dbt con BigQuery

Seleccionar la estrategia de persistencia errónea en BigQuery impacta directamente en el consumo de slots de computación y en los terabytes escaneados por consulta. A continuación se detallan los trade-offs técnicos:

Estrategia dbt Operación en BigQuery Consumo de Slots / TB Riesgo de Concurrencia Caso de Uso Recomendado
Table / View CREATE OR REPLACE TABLE/VIEW Alto (recomputa el 100% del histórico en cada ejecución). Bajo (reemplazo atómico). Tablas dimensionales pequeñas (dim_users, dim_products < 5 GB).
Incremental (merge) MERGE INTO target USING staging Medio-Alto (escanea todo el destino si no se acotan particiones). Medio (bloqueo por mutación de registros concurrentes). Updates tardíos y mutaciones impredecibles en datasets moderados.
Incremental (insert_overwrite) MERGE estático o partición directa con partitions to replace Óptimo (solo procesa y reemplaza las particiones afectadas). Bajo (mutación aislada por partición de fecha o ID). Tablas de hechos masivas (fct_events, transacciones, telemetría IoT).
Microbatch Ejecución granular por ventana temporal independiente Muy bajo y predecible (paralelizable por intervalos discretos). Nulo (cargas batch acotadas cronológicamente). Cargas de streaming orquestadas y backfills controlados de gran escala.
⚠️ FinOps Warning: El antipatrón del is_incremental() sin acotar particiones

Uno de los errores más costosos en arquitecturas dbt + BigQuery es utilizar where {{ is_incremental() }} filtrando únicamente sobre la tabla de origen (source/staging), sin aplicar un filtro estricto sobre las particiones de la tabla de destino (target).

Impacto técnico: Al ejecutar un MERGE por defecto, BigQuery evalúa las claves primarias contra todas las particiones del destino, escaneando petabytes innecesariamente. Solución: Configurar incremental_strategy='insert_overwrite' y definir explícitamente partitions o filtros dinámicos con _dbt_max_partition.

Implementación Práctica: Modelo Incremental de Producción

A continuación se presenta un modelo analítico de hechos estructurado bajo la estrategia insert_overwrite dinámico en BigQuery, con particionamiento diario, clustering multinivel y filtrado de particiones acotado:

models/marts/fct_customer_transactions.sql SQL + Jinja
{{
  config(
    materialized = 'incremental',
    incremental_strategy = 'insert_overwrite',
    partition_by = {
      "field": "transaction_date",
      "data_type": "date",
      "granularity": "day"
    },
    cluster_by = ["tenant_id", "payment_status", "customer_id"],
    partitions = partitions_to_replace(),
    require_partition_filter = true
  )
}}

{% macro partitions_to_replace() %}
  {% if is_incremental() %}
    {% set get_partitions %}
      SELECT DISTINCT DATE(transaction_timestamp) AS partition_date
      FROM {{ ref('stg_payments_events') }}
      WHERE transaction_timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 3 DAY)
    {% endset %}
    {% set results = run_query(get_partitions) %}
    {% if execute %}
      {% set partition_list = results.columns[0].values() %}
      {{ return(partition_list) }}
    {% endif %}
  {% else %}
    {{ return([]) }}
  {% endif %}
{% endmacro %}

WITH raw_events AS (
    SELECT
        event_id,
        tenant_id,
        customer_id,
        payment_status,
        amount_eur,
        transaction_timestamp,
        DATE(transaction_timestamp) AS transaction_date
    FROM {{ ref('stg_payments_events') }}
    WHERE 1 = 1
    {% if is_incremental() %}
      -- Acota el escaneo en el dataset de origen durante runs diarios
      AND transaction_timestamp >= TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL 3 DAY)
    {% endif %}
),

validated_transactions AS (
    SELECT
        event_id AS transaction_id,
        tenant_id,
        customer_id,
        payment_status,
        amount_eur,
        transaction_date,
        transaction_timestamp,
        CURRENT_TIMESTAMP() AS dbt_loaded_at
    FROM raw_events
    WHERE amount_eur IS NOT NULL
)

SELECT * FROM validated_transactions

Patrones de Diseño y Gobierno en BigQuery

Para mantener una infraestructura de datos mantenible y tolerante a fallos cuando la escala de datos crece a decenas de terabytes diarios, es mandatorio aplicar los siguientes pilares:

1. Clustering Estratégico

BigQuery permite hasta 4 columnas de clustering por tabla. El orden de las columnas en cluster_by debe reflejar la selectividad de los filtros más habituales (desde mayor cardinalidad o igualdad constante hasta agregaciones GROUP BY).

2. Forzar Filtrado de Particiones (require_partition_filter)

Establece require_partition_filter: true en las tablas de hechos mart. Esto impide que analistas o herramientas de BI (Looker, Tableau, Power BI) ejecuten queries ad-hoc sin especificar una cláusula WHERE sobre la columna particionada, protegiendo el presupuesto de la organización.

3. Slim CI con Caché de Estado (--defer)

En los pipelines de integración continua (GitHub Actions o GitLab CI), nunca ejecutes dbt run sobre el warehouse completo. Utiliza dbt build --select state:modified+ --defer --state ./prod_manifest para compilar exclusivamente los modelos que han sufrido cambios en la rama activa.

Framework de Implementación Enterprise

Sigue esta metodología para orquestar y desplegar modelos de dbt sobre Google BigQuery con estándares de ingeniería de software:

1
Definición de Capas Semánticas y Nombres de Dataset:

Aislar entornos lógicos en BigQuery mediante el archivo dbt_project.yml y custom schemas (ej: raw_staging, int_transforms, analytics_marts).

2
Configuración de Conexión via Service Account con Least Privilege:

Otorgar únicamente los roles de BigQuery Data Editor y BigQuery Job User a la cuenta de servicio de dbt, restringiendo accesos administrativos innecesarios.

3
Auditoría de Esquemas con Contratos (Model Contracts):

Declarar contract: {enforced: true} y tipos de datos explícitos en los archivos YAML para evitar desviaciones silenciosas de schema que rompan el pipeline analítico downstream.

4
Monitoreo de Costes y Performance con INFORMATION_SCHEMA:

Implementar un modelo dbt recurrente que consulte `region-eu`.INFORMATION_SCHEMA.JOBS_BY_PROJECT para auditar slots consumidos y bytes facturados por modelo ejecutado.

Preguntas Frecuentes (FAQ)

¿Cuál es la diferencia de coste y rendimiento entre la estrategia incremental 'merge' e 'insert_overwrite' en dbt con BigQuery?

La estrategia merge realiza un escaneo comparativo de la tabla destino para evaluar condiciones de coincidencia, consumiendo slots y bytes escaneados en datasets masivos. Por el contrario, insert_overwrite sobre particiones específicas reemplaza particiones enteras sin necesidad de realizar comparaciones fila a fila, reduciendo drásticamente el escaneo de TBs y evitando bloqueos concurrentes de mutación.

¿Cómo previene dbt el desperdicio de slots y bytes escaneados en entornos de CI/CD sobre BigQuery?

Se implementa dbt Slim CI mediante el uso del flag --defer --state, lo que permite compilar y ejecutar únicamente los modelos modificados y sus dependencias directas en datasets efímeros de staging, limitando además los rangos de partición mediante macros condicionales con variables de entorno para no escanear particiones históricas en validaciones de Pull Requests.

¿Qué orden de clustering se debe configurar en un modelo dbt de BigQuery?

BigQuery admite hasta 4 columnas de clustering por tabla. El orden en la configuración de dbt (cluster_by) debe respetar estrictamente la cardinalidad y los patrones de filtrado más frecuentes en las consultas analíticas, colocando primero las columnas con mayor selectividad o las claves de agregación y filtrado más recurrentes.

Sobre el Autor: Eduardo Martínez Agrelo

AI & Data Architect

Especialista en diseño de arquitecturas de datos modernas, analítica a gran escala e Inteligencia Artificial en entornos Cloud. Con amplia experiencia liderando transformaciones tecnológicas en plataformas cloud como Google BigQuery, Snowflake, Databricks y dbt para optimizar rendimiento, gobernanza y costes operativos.