Snowflake MigrationClickHouse Workshops

05 Benchmark et bascule

Reconstruisez les tableaux de bord dans ClickHouse, comparez les sept requêtes sur les deux moteurs, basculez le producteur, vérifiez la parité et supprimez les environnements.

Point de départ

Module 04 terminé : la couche analytics est remplie et testée. analytics.fact_trips contient environ 50 millions de lignes ; analytics.dim_taxi_zones, analytics.dim_payment_type, analytics.dim_vendor et analytics.dim_date sont entièrement chargées ; dbt test réussit de bout en bout ; et analytics.taxi_zones_dict est actif et renvoie les arrondissements avec dictGet(). analytics.agg_hourly_zone_trips reste vide — par conception, pas par défaut — jusqu’à la bascule de ce module. Le producteur Snowflake fonctionne encore et l’écart entre Snowflake et ClickHouse reste ouvert. Prévoyez environ 45 minutes.

Pourquoi

Tous les modules précédents ont servi de préparation. Le module 03 a prouvé que ClickHouse pouvait contenir 50 millions de lignes ; le module 04, que le pipeline dbt pouvait s’y exécuter. Aucun ne suffit à lui seul pour qu’un partenaire valide une « migration » : il manque encore une mesure et une bascule.

La mesure est le benchmark de l’étape 2 : les sept mêmes requêtes que dans votre plan de migration, exécutées l’une après l’autre dans Snowflake et ClickHouse, avec la médiane de trois passages sur chaque moteur. C’est ce qui transforme « ClickHouse devrait être plus rapide » en un gain précis et défendable qu’un partenaire peut présenter à ses propres parties prenantes.

La bascule de l’étape 3 complète le travail. Jusqu’ici, les deux systèmes ont fonctionné côte à côte : Snowflake comme système de référence, ClickHouse rattrapant son retard. Une migration qui ne déplace jamais réellement le chemin d’écriture reste une copie, pas une migration. L’étape 3 arrête le producteur Snowflake, referme l’écart entretenu par ses écritures continues depuis le module 01, puis commence à écrire les nouvelles courses dans ClickHouse — le moment où ClickHouse devient le système de référence.

C’est également le dernier module participant avant l’évaluation écrite du module 06, réalisée à livre ouvert à partir de vos productions. Les tableaux de bord, le CSV du benchmark et la vérification de parité doivent être réels avant de supprimer quoi que ce soit à l’étape 5.

Concepts — fonctionnement interne

La couche BI, concrètement. À l’étape 1, bash superset/add_clickhouse_connection.sh appelle directement l’API REST de Superset ; aucun parcours manuel de l’interface n’est nécessaire. Le script enregistre la connexion ClickHouse, puis importe l’export de tableau de bord fourni, ajoutant quatre tableaux ClickHouse aux trois tableaux Snowflake créés au module 01 (sept au total) :

Tableau de bordReproduitCe qu’il démontre
CH — Operations Command CenterSnowflake Dashboard 1Données actives de fact_trips (après la bascule) ; mêmes KPI, requêtes plus rapides
CH — Executive Weekly ReportSnowflake Dashboard 2QUALIFY réécrit sous forme de sous-requête avec ROW_NUMBER()
CH — Driver & Quality AnalyticsSnowflake Dashboard 3JSONExtractString à la place de LATERAL FLATTEN de Snowflake
CH — Capabilities Showcase(nouveau — aucun équivalent Snowflake)Fonctions approximatives, jointures par dictionnaire et clause SAMPLE

Ce que sollicitent les sept requêtes du benchmark. L’étape 2 exécute les sept mêmes requêtes que votre plan sur les deux moteurs et compare le temps réel écoulé. Chacune cible une différence de dialecte ou une capacité du moteur relevée au module 02 :

RequêteÉlément sollicité
Q1Chiffre d’affaires horaire par arrondissement
Q2Distance moyenne glissante sur 7 jours
Q310 principales courses — QUALIFY de Snowflake contre sous-requête ROW_NUMBER() dans ClickHouse
Q4Notes des chauffeurs — LATERAL FLATTEN de Snowflake contre JSONExtractString de ClickHouse
Q5Tarification dynamique — VARIANT de Snowflake contre String + JSONExtract* de ClickHouse
Q6Agrégation horaire — MERGE de Snowflake contre ReplacingMergeTree de ClickHouse
Q7CDC / fraîcheur des données actives

L’écart de bascule. Le producteur Snowflake écrit environ 60 courses par minute depuis le module 01 sans jamais s’arrêter. Le script du module 03 a capturé TRIPS_RAW dans l’état où elle se trouvait au moment de son exécution, puis le module 04 a construit le pipeline dbt au-dessus de cet instantané. Toutes les courses ajoutées depuis le dernier lot n’existent que dans Snowflake : la fin manque dans ClickHouse. Le passage de rattrapage --resume de l’étape 3 referme précisément cet écart : il lit le max(pickup_at) déjà présent dans ClickHouse et ne récupère que les lignes écrites après. Le rattrapage d’un écart ouvert depuis le module 01 prend ainsi quelques secondes ou minutes, pas les 40 à 50 minutes de la migration en masse initiale. Si vous l’ignorez et basculez quand même, ClickHouse perd définitivement toutes les courses arrivées dans l’intervalle — une erreur de parité silencieuse que l’étape 4 est conçue pour détecter, mais seulement si l’étape 3 a été exécutée dans l’ordre.

agg_hourly_zone_trips se remplit ici, et uniquement ici. Elle est vide depuis le module 03 par conception : son filtre incrémentiel WHERE pickup_at >= now() - INTERVAL 2 HOUR ne correspond qu’aux lignes écrites par un producteur actif, et jusqu’ici le seul producteur écrivait dans Snowflake. Dès que l’étape 3 démarre le producteur ClickHouse, les nouvelles lignes entrent enfin dans cette fenêtre de deux heures. La table — et tous les graphiques qui en dépendent — cesse alors d’être vide pour la première fois de l’atelier.

Étape 1 — Ajouter les tableaux de bord ClickHouse

Pour réaliser manuellement toute la construction — création pas à pas des 7 jeux de données, 18 graphiques et 4 tableaux de bord dans l’interface Superset —, suivez Superset sur ClickHouse. Pour ignorer ces étapes et tout importer en une seule fois :

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

L’hôte ClickHouse contenu dans le fichier superset/dashboards/dashboard_export_*.zip du dépôt a été remplacé par your-instance.clickhouse.cloud. Le raccourci ci-dessus corrige l’URI depuis .env avant l’import, de façon transparente. En revanche, si vous importez le ZIP manuellement dans l’interface Superset, la connexion créée ne fonctionnera pas : vous devrez ensuite la modifier afin qu’elle utilise le véritable CLICKHOUSE_HOST et les bons identifiants. Consultez Superset sur ClickHouse pour les instructions précises.

Vérification :

Ouvrez http://localhost:8088 (admin / admin). Sous Dashboards, vous devez voir 7 éléments au total : 3 tableaux de bord Snowflake et 4 dont le nom commence par CH —.

Étape 2 — Exécuter le benchmark

Exécutez les sept requêtes successivement dans Snowflake et ClickHouse, puis comparez le temps écoulé :

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

Sortie attendue :

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  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
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Ces chiffres correspondent à une exécution représentative, pas à une garantie. Vos résultats varieront selon la taille du warehouse, le niveau ClickHouse Cloud et les autres charges présentes sur les services au même moment.

Le script écrit chaque exécution dans workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Conservez le fichier sur le disque : c’est l’un des deux fichiers nécessaires à l’évaluation du module 06. Ne le supprimez pas lors du nettoyage de l’étape 5.

Étape 3 — Basculer vers ClickHouse

Écart de migration. Le producteur Snowflake est resté actif pendant tout l’atelier, avec environ 60 courses par minute. Le script du module 03 a capturé TRIPS_RAW telle qu’elle était au moment de son exécution ; les lignes ajoutées ensuite n’existent que dans Snowflake. Refermez cet écart avant de déplacer le chemin d’écriture.

Exécutez les quatre opérations suivantes dans l’ordre indiqué — cet ordre assure la cohérence des deux systèmes :

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

Ne sautez pas l’étape 2. --resume lit le max(pickup_at) déjà présent dans ClickHouse et ajoute un filtre WHERE PICKUP_DATETIME > <watermark> à la requête Snowflake. Il ne transfère ainsi que les lignes écrites pendant et après la migration initiale du module 03 — celles que ClickHouse n’a jamais vues. Une bascule sans ce passage ferait perdre définitivement toutes les courses de cette fenêtre. La vérification de parité de l’étape 4 est conçue pour le détecter, mais seulement si cette étape est exécutée d’abord.

./scripts/03_cutover.sh affiche l’invite Type "cutover" to confirm, puis répète les étapes 1 à 3 comme protection supplémentaire : il arrête à nouveau le producteur Snowflake (sans effet s’il est déjà arrêté), exécute un autre dbt run, puis construit et démarre le producteur ClickHouse (nyc_taxi_ch_producer). Trente secondes après son démarrage, il vérifie l’arrivée de nouvelles lignes dans default.trips_raw et relance dbt une dernière fois — l’exécution qui donne enfin ses premières lignes à agg_hourly_zone_trips et referme l’écart volontairement laissé par le module 04.

Vérification :

-- 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

Maintenir la fraîcheur de la couche analytics. fact_trips et agg_hourly_zone_trips sont des modèles dbt incrémentiels : ils ne s’actualisent pas automatiquement. 03_cutover.sh exécute dbt run une fois après avoir confirmé l’activité du producteur, mais les tableaux de bord prennent du retard à mesure que les courses s’accumulent. Relancez dbt run depuis dbt/nyc_taxi_dbt_ch chaque fois que vous voulez des chiffres à jour (en production, vous le planifieriez avec cron, Airflow ou dbt Cloud ; à la demande suffit ici). À l’inverse, analytics.mv_live_trip_feed est une vue matérialisée actualisable — le dbt run du module 04 l’a déjà créée avec engine = 'ReplacingMergeTree(refreshed_at)' — mais l’atelier n’active jamais son intervalle : l’instruction MODIFY REFRESH EVERY 30 SECOND qui permettrait sa réexécution autonome n’existe que comme commentaire dans le modèle. Vous pouvez l’activer vous-même par une unique instruction ALTER TABLE ; sans cela, mv_live_trip_feed n’est actualisée qu’une fois, lors de sa création par dbt.

Bascule inverse, si vous devez annuler cette étape et revenir au producteur 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

Étape 4 — Vérifier la parité

Maintenant que le rattrapage --resume a été effectué et que le producteur ClickHouse est actif, les deux systèmes doivent être à parité. Confirmez-le :

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

Sortie attendue :

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
  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
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

Votre nombre de lignes sera différent ; c’est la ligne de parité qui compte. Le producteur Snowflake est désormais arrêté, donc aucune nouvelle ligne n’y arrive. Les comptages doivent être identiques ou ne différer que de quelques lignes si un lot était encore en cours pendant le passage --resume, bien sous le seuil de 0,01 % vérifié par le script.

Si la vérification de parité échoue (écart supérieur à 0,01 %), l’écart n’a pas été entièrement refermé. Relancez le rattrapage, puis vérifiez à nouveau :

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

Étape 5 — Supprimer les environnements

Avant toute suppression, confirmez que la migration se trouve dans un état final correct :

VérificationCommandeRésultat attendu
Parité du nombre de lignesbash scripts/01_verify_migration.shCorrespondance ≥ 99,9 %
Tests dbtdbt test (depuis dbt/nyc_taxi_dbt_ch)Tous les tests réussissent
Tableaux de bord SupersetOuvrir http://localhost:80887 tableaux visibles (3 SF + 4 CH)
Résultats du benchmarkcat scripts/benchmark_results_<timestamp>.csvLes 7 requêtes ont une valeur de gain
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

Lorsque les quatre vérifications réussissent, deux fichiers suffisent au module 06 et survivent à la suppression : workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md et workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Le module 06 est une évaluation écrite à livre ouvert d’environ 60 minutes qui n’a besoin de rien d’autre : inutile de laisser fonctionner un service ClickHouse Cloud payant pendant un examen écrit. Copiez ou notez le contenu des deux fichiers dans un endroit accessible, puis supprimez tout :

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

Cette commande détruit le service ClickHouse Cloud (avec terraform destroy) et, si la bascule a eu lieu, le conteneur du producteur de courses ClickHouse.

Les ressources Snowflake de la partie 1 ne sont pas supprimées par ce script. Supprimez séparément la partie Snowflake :

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

Comment vérifier que vous avez terminé

À ce stade du module, vous devez avoir confirmé, dans l’ordre :

  • Réussite de la vérification de parité — 01_verify_migration.sh à l’étape 4 a indiqué PASS avec un écart de nombre de lignes inférieur à 0,01 %.
  • CSV du benchmark présent sur le disque — l’étape 2 a écrit benchmark_results_<timestamp>.csv avec une valeur de gain pour chacune des 7 requêtes, et vous l’avez conservé avant la suppression de l’étape 5.
  • Présence des 7 tableaux de bord — la vérification Superset de l’étape 1 a montré côte à côte 3 tableaux Snowflake et 4 tableaux CH —.
  • Écriture du producteur ClickHouse — le bloc de vérification de l’étape 3 a montré que default.trips_raw recevait des lignes et que nyc_taxi_ch_producer fonctionnait, avant son arrêt par l’étape 5.

Si l’une de ces conditions n’était pas remplie à ce moment-là, revenez à l’étape correspondante au lieu de relancer ces vérifications maintenant : l’étape 5 a déjà détruit le service ClickHouse Cloud et, si la bascule a eu lieu, le conteneur du producteur.

État final

La migration est terminée et mesurée : 50 millions de lignes ont été déplacées de Snowflake vers ClickHouse et vérifiées à parité ; les sept requêtes ont été comparées directement, ClickHouse étant plus rapide pour chacune ; la couche BI a été reconstruite avec 4 tableaux de bord ClickHouse à côté des 3 tableaux de bord Snowflake d’origine ; et le chemin d’écriture a définitivement basculé de Snowflake vers ClickHouse. Les deux environnements cloud ont été supprimés : plus de service ClickHouse Cloud, plus de conteneur du producteur ClickHouse et, après la suppression de la partie 1, plus de warehouse Snowflake.

Deux fichiers survivent et suffisent au module 06 : workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md et workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Le module 06 est une évaluation écrite à livre ouvert : apportez uniquement ces deux fichiers.

Sur cette page

Suivre votre progression ?

Facultatif. Nous envoyons un lien par e-mail pour confirmer votre adresse ; la progression est enregistrée après son ouverture.

Utilisez votre adresse e-mail professionnelle, et non une adresse personnelle.

Le suivi de la progression exige aussi d’accepter les Conditions d’utilisation actuelles dans les Paramètres de confidentialité.

FR