Snowflake MigrationClickHouse Workshops

05 Benchmark y transición

Reconstruye paneles sobre ClickHouse, compara siete consultas, transfiere el productor, verifica paridad y desmonta.

Punto de partida

Módulo 04 completado: la capa de analytics está poblada y probada. analytics.fact_trips contiene aproximadamente 50 millones de filas; analytics.dim_taxi_zones, analytics.dim_payment_type, analytics.dim_vendor y analytics.dim_date están completamente cargadas; dbt test pasa de principio a fin; y analytics.taxi_zones_dict está activo y devuelve distritos mediante dictGet(). analytics.agg_hourly_zone_trips sigue vacía —por diseño, no por un defecto— y permanecerá así hasta la transición de este módulo. El productor de Snowflake continúa en ejecución y el desfase entre Snowflake y ClickHouse sigue abierto. Reserva unos 45 minutos.

Por qué

Todos los módulos anteriores han sido una preparación. El módulo 03 demostró que ClickHouse puede almacenar 50 millones de filas; el módulo 04, que la canalización dbt funciona sobre ellas. Ninguno de los dos, por sí solo, bastaría para que un participante aprobara la migración como terminada: todavía hacen falta dos cosas, una cifra y una transición.

La cifra es el benchmark del Paso 2: las mismas siete consultas de tu plan de migración, ejecutadas consecutivamente en Snowflake y ClickHouse, con la mediana de tres ejecuciones en cada motor. Esto convierte «ClickHouse debería ser más rápido» en una aceleración concreta y defendible que un participante puede presentar a sus propios responsables.

La transición del Paso 3 es la otra mitad. Hasta ahora, ambos sistemas han funcionado en paralelo: Snowflake como sistema de registro y ClickHouse recuperando el retraso. Una migración que nunca mueve realmente la ruta de escritura es una copia, no una migración. El Paso 3 detiene el productor de Snowflake, cierra el desfase que sus escrituras continuas mantienen abierto desde el módulo 01 y empieza a escribir los viajes nuevos en ClickHouse. Ese es el momento en que ClickHouse se convierte en el sistema de registro.

También es el último módulo del participante antes de la evaluación escrita del módulo 06, que es a libro abierto y se basa en lo que produzcas aquí. Los paneles, el CSV del benchmark y la comprobación de paridad deben ser reales antes de desmontar nada en el Paso 5.

Conceptos internos

Funcionamiento mecánico de la capa BI. En el Paso 1, bash superset/add_clickhouse_connection.sh llama directamente a la API REST de Superset; no hay que recorrer manualmente la interfaz. Registra la conexión de ClickHouse y después importa la exportación de paneles incluida en el repositorio, añadiendo cuatro paneles de ClickHouse junto a los tres de Snowflake que creó el módulo 01, siete en total:

PanelReflejaDemuestra
CH — Operations Command CenterPanel Snowflake 1Datos en vivo de fact_trips después de la transición; mismos KPI, consultas más rápidas
CH — Executive Weekly ReportPanel Snowflake 2QUALIFY reescrito como subconsulta de ROW_NUMBER()
CH — Driver & Quality AnalyticsPanel Snowflake 3JSONExtractString en lugar del LATERAL FLATTEN de Snowflake
CH — Capabilities Showcase(nuevo, sin equivalente en Snowflake)Funciones aproximadas, uniones mediante diccionario y cláusula SAMPLE

Qué ejercitan las siete consultas del benchmark. El Paso 2 ejecuta las mismas siete consultas de tu plan de migración en ambos motores y compara el tiempo transcurrido. Cada una se dirige a una diferencia de dialecto o a una característica del motor que aparecía en el plan del módulo 02:

ConsultaEjercita
Q1Ingresos horarios por borough
Q2Media móvil de distancia a 7 días
Q3Los diez viajes principales: QUALIFY de Snowflake frente a una subconsulta ROW_NUMBER() de ClickHouse
Q4Valoraciones de conductores: LATERAL FLATTEN de Snowflake frente a JSONExtractString de ClickHouse
Q5Tarificación dinámica: VARIANT de Snowflake frente a String + JSONExtract* de ClickHouse
Q6Agregación horaria: MERGE de Snowflake frente a ReplacingMergeTree de ClickHouse
Q7CDC y frescura de los datos en vivo

El desfase de la transición. El productor de Snowflake ha escrito aproximadamente 60 viajes por minuto desde el módulo 01 y nunca se ha detenido. El script de migración del módulo 03 capturó TRIPS_RAW tal como estaba cuando se ejecutó, y el módulo 04 construyó la canalización dbt sobre esa instantánea. Cada viaje escrito en Snowflake desde el último lote del script existe únicamente allí: falta el final de los datos en ClickHouse. La pasada de recuperación --resume del Paso 3 cierra exactamente ese desfase: lee el max(pickup_at) ya presente en ClickHouse y recupera solo las filas escritas después. Así, cerrar un desfase abierto desde el módulo 01 tarda de segundos a minutos, y no los 40–50 minutos de la migración masiva original. Si omites la pasada y realizas la transición de todos modos, ClickHouse perderá definitivamente todos los viajes que llegaron durante el desfase: un fallo silencioso de paridad que el Paso 4 está diseñado para detectar, pero solo si el Paso 3 se ejecuta en orden.

agg_hourly_zone_trips se llena aquí y solo aquí. Lleva vacía desde el módulo 03 por diseño: su filtro incremental es WHERE pickup_at >= now() - INTERVAL 2 HOUR, que solo coincide con filas escritas por un productor en vivo, y hasta este módulo el único productor que escribía era el de Snowflake. Cuando el Paso 3 inicia el productor de ClickHouse, por fin llegan filas nuevas dentro de esa ventana de dos horas, y tanto la tabla como todos los gráficos que dependen de ella dejan de estar vacíos por primera vez en el laboratorio.

Paso 1 — Añade los paneles ClickHouse

Para realizar la construcción manual completa —crear uno a uno los 7 conjuntos de datos, los 18 gráficos y los 4 paneles en la interfaz de Superset—, sigue Superset sobre ClickHouse. Si prefieres omitir esos pasos e importarlo todo de una vez:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
bash superset/add_clickhouse_connection.sh

En el archivo superset/dashboards/dashboard_export_*.zip incluido en el repositorio, el host de ClickHouse se ha ocultado como your-instance.clickhouse.cloud. El atajo anterior corrige la URI a partir de .env antes de importar, de modo que el cambio es transparente y no notarás el valor oculto. Sin embargo, si importas el ZIP manualmente desde la interfaz de Superset, la conexión de base de datos que se crea no funcionará: tendrás que editarla después para que apunte al CLICKHOUSE_HOST real y utilice tus credenciales. Superset sobre ClickHouse explica el procedimiento exacto.

Verificación:

Abre http://localhost:8088 (admin / admin). En Dashboards debes ver 7 paneles en total: 3 de Snowflake y 4 cuyo nombre comienza por CH —.

Paso 2 — Ejecuta el benchmark

Ejecuta consecutivamente las siete consultas en Snowflake y ClickHouse y compara el tiempo transcurrido:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
./scripts/run_benchmark.sh

Salida esperada:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  NYC Taxi Lab — Query Benchmark: Snowflake vs ClickHouse
  (median of 3 runs each)
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
Query                                   Snowflake     ClickHouse    Speedup
────────────────────────────────────────────────────────────────────────
Q1  Hourly revenue by borough           5.0s          0.7s          6x
Q2  Rolling 7-day avg distance          5.5s          0.8s          6x
Q3  Top 10 trips (QUALIFY→subquery)     5.0s          0.7s          6x
Q4  Driver ratings (JSON flatten)       5.4s          0.8s          6x
Q5  Surge pricing (VARIANT)             5.1s          0.7s          6x
Q6  Hourly aggregation (MERGE→RMT)      5.9s          0.8s          7x
Q7  CDC/live data freshness             7.9s          0.8s          9x
────────────────────────────────────────────────────────────────────────
Total                                   40.1s         5.6s          7x avg
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Estas cifras corresponden a una ejecución representativa, no son una garantía. Tus resultados variarán en función del tamaño del warehouse, el nivel de ClickHouse Cloud y cualquier otra carga que se ejecute en cualquiera de los dos servicios.

El script escribe cada ejecución en workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Consérvalo en disco: es uno de los dos archivos que necesita la evaluación del módulo 06, por lo que no debes eliminarlo durante el desmontaje del Paso 5.

Paso 3 — Transfiere a ClickHouse

Desfase de migración. El productor de Snowflake ha permanecido activo durante todo el laboratorio y escribe aproximadamente 60 viajes por minuto. El script de migración del módulo 03 capturó TRIPS_RAW tal como estaba en el momento de su ejecución; las filas escritas desde entonces solo existen en Snowflake. Cierra ese desfase antes de trasladar la ruta de escritura.

Ejecuta los cuatro pasos siguientes en este orden: ese orden es lo que mantiene la coherencia entre los dos sistemas.

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
source .venv/bin/activate

# Step 1: Stop the Snowflake producer (freeze the dataset)
docker stop nyc_taxi_producer

# Step 2: Catch up the delta — only migrates rows with pickup_at newer than
# what's already in ClickHouse. Runs in seconds to minutes, not the original
# 40-50 minutes, because only the gap rows move.
python scripts/02_migrate_trips.py --resume

# Step 3: Refresh the analytics tables with the newly migrated rows
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/dbt/nyc_taxi_dbt_ch"
dbt run
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"

# Step 4: Start the ClickHouse producer
source .env && source .clickhouse_state
./scripts/03_cutover.sh

No omitas el Paso 2. --resume lee el max(pickup_at) ya presente en ClickHouse y añade a la consulta de Snowflake un filtro WHERE PICKUP_DATETIME > <watermark>. De este modo, solo transfiere las filas escritas durante y después de la migración original del módulo 03: aquellas que ClickHouse nunca ha visto. Si realizas la transición sin esa pasada, ClickHouse perderá definitivamente todos los viajes que llegaron durante la ventana. La comprobación de paridad del Paso 4 está diseñada para detectar precisamente ese problema, pero solo si este paso se ha ejecutado antes.

./scripts/03_cutover.sh muestra la solicitud Type "cutover" to confirm y después repite los Pasos 1–3 como medida de seguridad propia: vuelve a detener el productor de Snowflake, una operación sin efecto si ya estaba detenido, ejecuta otro dbt run y construye e inicia el productor de ClickHouse (nyc_taxi_ch_producer). Treinta segundos después de iniciarlo, confirma que están llegando filas nuevas a default.trips_raw y ejecuta dbt una vez más: esta es la ejecución que por fin introduce las primeras filas en agg_hourly_zone_trips y cierra el desfase que el módulo 04 dejó abierto por diseño.

Verificación:

-- Most recent trip should be within the last 60 seconds
SELECT max(pickup_at) AS most_recent_trip FROM default.trips_raw;

-- Row count should be increasing — wait 60 seconds and run again
SELECT count() FROM default.trips_raw;

-- agg_hourly_zone_trips should now have rows for the first time in the lab
SELECT count() FROM analytics.agg_hourly_zone_trips;
docker ps | grep nyc_taxi_ch_producer   # should show running

Mantener actualizada la capa de analytics. fact_trips y agg_hourly_zone_trips son modelos incrementales de dbt: no se actualizan de forma automática. 03_cutover.sh ejecuta dbt run una vez después de confirmar que el productor está activo, pero los paneles quedan desactualizados a medida que se acumulan nuevos viajes. Vuelve a ejecutar dbt run desde dbt/nyc_taxi_dbt_ch siempre que necesites cifras actuales. En producción programarías esta ejecución con cron, Airflow o dbt Cloud; para el laboratorio basta con hacerlo bajo demanda. En cambio, analytics.mv_live_trip_feed es una vista materializada actualizable: el dbt run del módulo 04 ya la construyó con engine = 'ReplacingMergeTree(refreshed_at)', pero el laboratorio nunca activa su intervalo de actualización. La instrucción MODIFY REFRESH EVERY 30 SECOND que haría que se reexecutara sola solo aparece como comentario en el archivo del modelo. Puedes activarla tú mismo con una única instrucción ALTER TABLE; sin ella, mv_live_trip_feed solo se actualiza cuando dbt la construye.

Transición inversa, si necesitas deshacer este paso y volver al productor de Snowflake:

docker stop nyc_taxi_ch_producer
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake/superset"
docker-compose --env-file ../.env up -d producer

Paso 4 — Verifica paridad

Ahora que se ha ejecutado la pasada de recuperación --resume y el productor de ClickHouse está activo, ambos sistemas deberían estar en paridad. Confírmalo:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
bash scripts/01_verify_migration.sh

Salida esperada:

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  Migration Parity Check
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

  ✓ ClickHouse default.trips_raw: 50,008,250 rows

  ✓ Snowflake NYC_TAXI_DB.RAW.TRIPS_RAW: 50,008,250 rows
  ✓ Row count parity: PASS  (difference: 0 rows = 0.0000%)

  ✓ trip_metadata populated: 50,008,250 non-empty rows
  pickup_at range: 2022-03-30   2026-03-31

  ✓ ClickHouse has 50,008,250 rows — migration looks complete
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Tu propio número de filas será distinto; lo que importa es la línea de paridad. A estas alturas, el productor de Snowflake está detenido y allí ya no llegan filas nuevas. Los recuentos deberían coincidir exactamente o diferir en unas pocas filas si había un lote en curso durante la pasada --resume, siempre dentro del umbral del 0,01 % que comprueba el script.

Si falla la comprobación de paridad, con una diferencia superior al 0,01 %, el desfase no se cerró por completo. Ejecuta de nuevo la pasada de recuperación y vuelve a comprobar:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
python scripts/02_migrate_trips.py --resume
bash scripts/01_verify_migration.sh

Paso 5 — Desmonta

Antes de desmontar nada, confirma que la migración se encuentra en un estado final correcto:

ControlComandoEsperado
Paridad del número de filasbash scripts/01_verify_migration.shCoincidencia del número de filas ≥ 99,9 %
Pruebas de dbtdbt test (desde dbt/nyc_taxi_dbt_ch)Todas las pruebas pasan
Paneles de SupersetAbre http://localhost:80887 paneles visibles (3 SF + 4 CH)
Resultados del benchmarkcat scripts/benchmark_results_<timestamp>.csvLas 7 consultas tienen un valor de aceleración
cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && source .clickhouse_state
bash scripts/01_verify_migration.sh

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/dbt/nyc_taxi_dbt_ch"
dbt test

Cuando pasen las cuatro comprobaciones, dos archivos contienen todo lo que necesita el módulo 06 y ambos sobreviven al desmontaje: workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md y workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. El módulo 06 es una evaluación escrita a libro abierto de unos 60 minutos y no necesita nada más. No hay motivo para mantener un servicio de ClickHouse Cloud de pago durante un examen escrito. Copia o anota el contenido de ambos archivos en un lugar al que puedas acceder y después desmonta todo:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse"
source .env && ./teardown.sh

Esto destruye el servicio de ClickHouse Cloud mediante terraform destroy y el contenedor del productor de viajes de ClickHouse, si ya se realizó la transición.

Este script no desmonta los recursos Snowflake de la primera parte. Desmonta el lado Snowflake por separado:

cd "$(git rev-parse --show-toplevel)/workshop_public/snowflake_migration_lab/01-setup-snowflake"
source .env && ./teardown.sh

Cómo verificar la finalización

En este punto del módulo debes haber confirmado, en este orden:

  • Comprobación de paridad aprobada: el 01_verify_migration.sh del Paso 4 informó PASS con una diferencia de recuento inferior al 0,01 %.
  • CSV del benchmark en disco: el Paso 2 escribió benchmark_results_<timestamp>.csv con un valor de aceleración para las 7 consultas, y lo conservaste antes del desmontaje del Paso 5.
  • 7 paneles presentes: la comprobación de Superset del Paso 1 mostró juntos 3 paneles de Snowflake y 4 paneles CH —.
  • Productor de ClickHouse escribiendo: el bloque de verificación del Paso 3 mostró que default.trips_raw seguía aumentando y que nyc_taxi_ch_producer estaba activo, antes de que el Paso 5 lo detuviera para el desmontaje.

Si alguna condición no se cumplía en ese momento, vuelve al paso correspondiente en vez de intentar repetir ahora estas comprobaciones: el Paso 5 ya ha destruido el servicio de ClickHouse Cloud y, si se realizó la transición, también el contenedor del productor.

Estado final

La migración está terminada y medida: se trasladaron 50 millones de filas de Snowflake a ClickHouse y se verificó su paridad; las siete consultas se compararon directamente y ClickHouse fue más rápido en todas; la capa BI se reconstruyó con 4 paneles de ClickHouse junto a los 3 paneles originales de Snowflake; y la ruta de escritura pasó definitivamente de Snowflake a ClickHouse. Ambos entornos cloud están desmontados: no queda ningún servicio de ClickHouse Cloud ni contenedor del productor de ClickHouse y, una vez ejecutado también el desmontaje de la primera parte, tampoco ningún warehouse de Snowflake.

Dos archivos sobreviven al desmontaje y contienen todo lo que necesita el módulo 06: workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md y workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. El módulo 06 es una evaluación escrita a libro abierto: lleva únicamente esos dos archivos.

En esta página

¿Quieres seguir tu progreso?

Opcional. Enviaremos un enlace por correo para confirmar tu dirección; el progreso se registrará cuando lo abras.

Usa tu correo de trabajo, no uno personal.

Para seguir el progreso también debes aceptar los Términos del servicio actuales en la Configuración de privacidad.

ES