Snowflake MigrationClickHouse Workshops

05 Benchmark và cutover

Dựng lại các dashboard trên ClickHouse, benchmark cả bảy truy vấn trên cả hai engine, cutover producer, kiểm chứng tính tương đương, và dỡ bỏ.

Điểm khởi đầu

Module 04 đã hoàn tất: tầng analytics đã có dữ liệu và đã được test. analytics.fact_trips chứa khoảng 50 triệu dòng; analytics.dim_taxi_zones, analytics.dim_payment_type, analytics.dim_vendor và analytics.dim_date đã được nạp đầy đủ; dbt test pass từ đầu đến cuối; và analytics.taxi_zones_dict đang hoạt động và trả về borough qua dictGet(). analytics.agg_hourly_zone_trips vẫn còn rỗng — theo thiết kế, không phải do lỗi — và giữ nguyên như vậy cho đến bước cutover của module này. Producer Snowflake vẫn đang chạy, và khoảng trống giữa Snowflake và ClickHouse vẫn còn mở. Hãy dự trù khoảng 45 phút.

Vì sao

Mọi module tính đến lúc này đều là chuẩn bị. Module 03 chứng minh ClickHouse có thể chứa 50 triệu dòng; module 04 chứng minh pipeline dbt chạy được trên nó. Bản thân từng cái, không cái nào là thứ một đối tác sẽ ký xác nhận là "đã di chuyển" — điều đó cần thêm hai thứ nữa: một con số, và một lần cutover.

Con số là bài benchmark ở Bước 2: cùng bảy truy vấn từ kế hoạch di chuyển của bạn, chạy liên tiếp trên Snowflake rồi trên ClickHouse, lấy trung vị của ba lượt cho mỗi bên. Đó là thứ biến "ClickHouse chắc là nhanh hơn" thành một mức tăng tốc cụ thể, có thể bảo vệ được, mà một đối tác có thể đặt trước mặt các bên liên quan của chính họ.

Bước cutover ở Bước 3 là nửa còn lại. Đến giờ mọi module đều giữ hai hệ thống chạy song song, Snowflake là hệ thống nguồn sự thật và ClickHouse chạy theo phía sau. Một cuộc di chuyển mà không bao giờ thực sự chuyển đường ghi thì là một bản copy, không phải một cuộc di chuyển. Bước 3 dừng producer Snowflake, đóng khoảng trống mà các lượt ghi liên tục của nó đã giữ mở từ module 01, và bắt đầu ghi các chuyến đi mới vào ClickHouse thay thế — khoảnh khắc ClickHouse trở thành hệ thống nguồn sự thật.

Đây cũng là module học viên cuối cùng trước bài đánh giá viết ở module 06, vốn được phép tra tài liệu dựa trên bất cứ thứ gì bạn tạo ra ở đây. Các dashboard, file CSV benchmark và phép kiểm tra tính tương đương đều cần là thật trước khi bạn dỡ bỏ bất cứ thứ gì ở Bước 5.

Khái niệm — bên dưới lớp vỏ

Tầng BI, về mặt cơ học. bash superset/add_clickhouse_connection.sh gọi trực tiếp Superset REST API ở Bước 1 — không có bước bấm chuột thủ công nào trong UI của Superset. Nó đăng ký kết nối ClickHouse, rồi import bản export dashboard đã commit, thêm bốn dashboard ClickHouse bên cạnh ba dashboard Snowflake mà module 01 đã dựng (tổng cộng bảy):

DashboardĐối ứng vớiNó minh họa điều gì
CH — Operations Command CenterSnowflake Dashboard 1Dữ liệu fact_trips trực tiếp (sau cutover); cùng KPI, truy vấn nhanh hơn
CH — Executive Weekly ReportSnowflake Dashboard 2QUALIFY được viết lại thành một subquery ROW_NUMBER()
CH — Driver & Quality AnalyticsSnowflake Dashboard 3JSONExtractString thay cho LATERAL FLATTEN của Snowflake
CH — Capabilities Showcase(mới — không có tương đương ở Snowflake)Các hàm xấp xỉ, join qua dictionary, mệnh đề SAMPLE

Bảy truy vấn benchmark rèn những gì. Bước 2 chạy cùng bảy truy vấn từ kế hoạch di chuyển của bạn trên cả hai engine và so sánh thời gian thực tế. Mỗi truy vấn nhắm vào một khoảng cách phương ngữ hoặc một tính năng engine cụ thể từ kế hoạch của module 02:

Truy vấnRèn điều gì
Q1Doanh thu theo giờ theo borough
Q2Khoảng cách trung bình cuốn 7 ngày
Q3Top 10 chuyến đi — QUALIFY của Snowflake so với subquery ROW_NUMBER() của ClickHouse
Q4Đánh giá tài xế — LATERAL FLATTEN của Snowflake so với JSONExtractString của ClickHouse
Q5Giá tăng cao điểm — VARIANT của Snowflake so với String + JSONExtract* của ClickHouse
Q6Tổng hợp theo giờ — MERGE của Snowflake so với ReplacingMergeTree của ClickHouse
Q7Độ tươi của dữ liệu CDC / dữ liệu trực tiếp

Khoảng trống khi cutover. Producer Snowflake đã ghi khoảng 60 chuyến mỗi phút từ module 01, và nó chưa bao giờ dừng. Script di chuyển của module 03 chụp lại TRIPS_RAW đúng như lúc script đó chạy, và module 04 dựng pipeline dbt trên bản chụp đó. Mọi chuyến đi được ghi vào Snowflake sau lô cuối cùng của script di chuyển chỉ tồn tại trong Snowflake — phần đuôi của ClickHouse đang thiếu. Lượt bắt kịp --resume ở Bước 3 đóng đúng khoảng trống đó: nó đọc max(pickup_at) đã có trong ClickHouse và chỉ lấy các dòng được ghi sau đó, nên một lượt đóng khoảng trống mở từ module 01 chỉ mất vài giây đến vài phút, chứ không phải 40-50 phút như lượt di chuyển khối lượng lớn ban đầu. Bỏ qua nó mà vẫn cutover, thì ClickHouse sẽ mất vĩnh viễn số chuyến đi rơi vào khoảng trống đó — một lỗi tương đương âm thầm mà Bước 4 được dựng ra để bắt, nhưng chỉ khi Bước 3 đã chạy theo đúng thứ tự.

agg_hourly_zone_trips được nạp dữ liệu ở đây, và chỉ ở đây. Nó đã rỗng từ module 03 theo đúng thiết kế: filter incremental của nó là WHERE pickup_at >= now() - INTERVAL 2 HOUR, chỉ khớp các dòng do một producer chạy trực tiếp ghi vào, và cho đến module này thì producer duy nhất đang ghi là của Snowflake. Khi Bước 3 khởi động producer ClickHouse, các dòng mới cuối cùng cũng rơi vào cửa sổ hai giờ đó, và bảng này — cùng mọi chart dashboard dựa trên nó — lần đầu tiên trong lab không còn trống.

Bước 1 — Thêm các dashboard ClickHouse

Để dựng thủ công đầy đủ — tạo từng cái trong 7 dataset, 18 chart và 4 dashboard theo từng bước trong UI của Superset — hãy làm theo Superset trên ClickHouse. Còn nếu muốn bỏ qua các bước thủ công và import mọi thứ trong một lượt:

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

File superset/dashboards/dashboard_export_*.zip đã commit có host ClickHouse được ẩn thành your-instance.clickhouse.cloud. Cách rút gọn ở trên vá URI từ .env trước khi import, nên chuyện này diễn ra trong suốt — bạn sẽ không nhận ra phần bị ẩn. Nhưng nếu bạn tự import file ZIP thủ công qua UI của Superset, kết nối database mà nó tạo ra sẽ không kết nối được — bạn phải sửa kết nối đó sau đó để trỏ đến CLICKHOUSE_HOST và credential thật của bạn. Xem Superset trên ClickHouse để biết chính xác cách làm.

Kiểm chứng:

Mở http://localhost:8088 (admin / admin). Trong mục Dashboards, bạn sẽ thấy tổng cộng 7 — 3 dashboard Snowflake và 4 dashboard có tiền tố CH —.

Bước 2 — Chạy benchmark

Chạy cả bảy truy vấn liên tiếp trên cả Snowflake và ClickHouse rồi so sánh thời gian thực tế:

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

Output mong đợi:

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

Các con số này là của một lượt chạy tiêu biểu, không phải một cam kết — số của bạn sẽ khác tùy theo kích thước warehouse, tier ClickHouse Cloud, và bất cứ thứ gì khác đang chạy trên một trong hai service lúc đó.

Script ghi mọi lượt chạy vào workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Hãy giữ nó trên đĩa — nó là một trong hai file mà bài đánh giá của module 06 cần, nên đừng xóa nó trong lúc dỡ bỏ ở Bước 5.

Bước 3 — Cutover sang ClickHouse

Khoảng trống di chuyển. Producer Snowflake đã chạy suốt lab này, ghi khoảng 60 chuyến mỗi phút. Script di chuyển của module 03 chụp lại TRIPS_RAW đúng như lúc script đó chạy — các dòng được ghi từ đó trở đi chỉ tồn tại trong Snowflake. Hãy đóng khoảng trống đó trước khi chuyển đường ghi sang.

Hãy chạy cả bốn bước dưới đây, theo thứ tự — chính thứ tự là thứ giữ cho hai hệ thống nhất quán:

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

Đừng bỏ qua Bước 2. --resume đọc max(pickup_at) đã có trong ClickHouse và thêm một filter WHERE PICKUP_DATETIME > <watermark> vào truy vấn Snowflake, nên nó chỉ chuyển các dòng được ghi trong và sau lượt di chuyển gốc của module 03 — những dòng ClickHouse chưa bao giờ thấy. Cutover mà không có nó thì ClickHouse mất vĩnh viễn số chuyến đi rơi vào cửa sổ đó. Phép kiểm tra tính tương đương ở Bước 4 được dựng ra để bắt đúng điều đó, nhưng chỉ khi bước này đã chạy trước.

./scripts/03_cutover.sh sẽ hỏi Type "cutover" to confirm, rồi lặp lại các bước 1-3 như lưới an toàn của riêng nó: nó dừng producer Snowflake lần nữa (không làm gì nếu bạn đã dừng), chạy thêm một lượt dbt run, rồi build và khởi động producer ClickHouse (nyc_taxi_ch_producer). Ba mươi giây sau khi producer khởi động, nó xác nhận các dòng mới đang đến default.trips_raw và chạy dbt thêm một lần — lượt chạy cuối cùng cũng cho agg_hourly_zone_trips những dòng đầu tiên, đóng lại khoảng trống mà module 04 đã cố tình để mở.

Kiểm chứng:

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

Giữ tầng analytics luôn tươi. fact_trips và agg_hourly_zone_trips là các model incremental của dbt — chúng không tự refresh. 03_cutover.sh chạy dbt run một lần sau khi xác nhận producer đang hoạt động, nhưng các dashboard sẽ trôi dần thành cũ khi các chuyến đi mới tích tụ; hãy chạy lại dbt run từ dbt/nyc_taxi_dbt_ch bất cứ khi nào bạn muốn số liệu hiện thời (ở production bạn sẽ lên lịch việc này — cron, Airflow, dbt Cloud — nhưng chạy theo yêu cầu là ổn cho lab). Ngược lại, analytics.mv_live_trip_feed là một materialized view có thể refresh — lệnh dbt run của module 04 đã dựng nó với engine = 'ReplacingMergeTree(refreshed_at)' — nhưng lab không bao giờ bật chu kỳ refresh của nó: câu lệnh MODIFY REFRESH EVERY 30 SECOND vốn sẽ làm nó tự chạy lại chỉ tồn tại dưới dạng một comment trong file model. Bật nó lên là một lệnh ALTER TABLE duy nhất mà bạn sẽ tự chạy; nếu không có nó, mv_live_trip_feed chỉ cập nhật đúng một lần lúc dbt dựng nó.

Cutover ngược lại, nếu bạn cần hoàn tác bước này và quay về producer 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

Bước 4 — Kiểm chứng tính tương đương

Giờ khi lượt bắt kịp --resume đã chạy và producer ClickHouse đang hoạt động, hai hệ thống lẽ ra đã tương đương. Hãy xác nhận điều đó:

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

Output mong đợi:

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

Số dòng của riêng bạn sẽ khác; điều quan trọng là dòng về tính tương đương. Producer Snowflake đã dừng ở thời điểm này, nên không có dòng mới nào đến đó — số dòng phải khớp chính xác, hoặc lệch vài dòng nếu một lô còn đang trên đường trong lúc lượt --resume chạy, vẫn nằm sâu trong ngưỡng 0.01% mà script kiểm tra.

Nếu phép kiểm tra tính tương đương thất bại (lệch hơn 0.01%), khoảng trống chưa được đóng hoàn toàn — hãy chạy lại lượt bắt kịp và kiểm tra lại:

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

Bước 5 — Dỡ bỏ

Trước khi dỡ bỏ bất cứ thứ gì, hãy xác nhận cuộc di chuyển đang ở trạng thái cuối cùng đúng đắn:

Phép kiểm traLệnhMong đợi
Tương đương số dòngbash scripts/01_verify_migration.shKhớp số dòng ≥ 99.9%
Các test dbtdbt test (từ dbt/nyc_taxi_dbt_ch)Mọi test đều pass
Dashboard SupersetMở http://localhost:8088Thấy 7 dashboard (3 SF + 4 CH)
Kết quả benchmarkcat scripts/benchmark_results_<timestamp>.csvCả 7 truy vấn đều có giá trị tăng tốc
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

Khi cả bốn đều pass, hai file là tất cả những gì module 06 cần, và cả hai đều sống sót qua việc dỡ bỏ: workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md và workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Module 06 là một bài đánh giá trên giấy được phép tra tài liệu, khoảng 60 phút, và không cần gì khác — không có lý do gì để giữ một service ClickHouse Cloud có phí chạy suốt thời gian làm một bài thi viết. Hãy copy hoặc ghi lại nội dung của cả hai file vào chỗ nào bạn với tới được, rồi dỡ bỏ mọi thứ:

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

Lệnh này phá hủy service ClickHouse Cloud (qua terraform destroy) và container producer ClickHouse, nếu cutover đã được thực hiện.

Các tài nguyên Snowflake của Phần 1 không bị script này dỡ bỏ. Hãy dỡ bỏ phía Snowflake riêng:

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

Cách kiểm tra bạn đã xong

Đến điểm này trong module, bạn phải đã xác nhận, theo thứ tự:

  • Phép kiểm tra tính tương đương pass — 01_verify_migration.sh ở Bước 4 báo PASS với mức lệch số dòng dưới 0.01%.
  • File CSV benchmark trên đĩa — Bước 2 đã ghi benchmark_results_<timestamp>.csv với cả 7 truy vấn đều mang một giá trị tăng tốc, và bạn đã giữ nó trước khi dỡ bỏ ở Bước 5.
  • Có 7 dashboard — phép kiểm tra Superset ở Bước 1 cho thấy 3 dashboard Snowflake và 4 dashboard CH — nằm cạnh nhau.
  • Producer ClickHouse đang ghi — khối kiểm chứng ở Bước 3 cho thấy default.trips_raw đang tăng số dòng và nyc_taxi_ch_producer đang chạy, trước khi Bước 5 dừng nó để dỡ bỏ.

Nếu có điều nào trong số đó không đúng vào lúc ấy, hãy quay lại bước tương ứng thay vì chạy lại các phép kiểm tra này bây giờ — Bước 5 đã phá hủy service ClickHouse Cloud, và nếu cutover đã diễn ra thì cả container producer cũng đi cùng nó.

Trạng thái kết thúc

Cuộc di chuyển đã hoàn tất và được đo lường: 50 triệu dòng đã chuyển từ Snowflake sang ClickHouse và được kiểm chứng là tương đương, bảy truy vấn được benchmark đối đầu với ClickHouse nhanh hơn ở mọi truy vấn, tầng BI được dựng lại với 4 dashboard ClickHouse bên cạnh 3 dashboard Snowflake ban đầu, và đường ghi đã cutover từ Snowflake sang ClickHouse một cách dứt khoát. Cả hai môi trường cloud đã được dỡ bỏ — không còn service ClickHouse Cloud, không còn container producer ClickHouse, và khi phần dỡ bỏ của Phần 1 cũng đã chạy, cũng không còn warehouse Snowflake.

Hai file sống sót qua việc dỡ bỏ và là tất cả những gì module 06 cần: workshop_public/snowflake_migration_lab/02-plan-and-design/migration-plan.md và workshop_public/snowflake_migration_lab/03-migrate-to-clickhouse/scripts/benchmark_results_<timestamp>.csv. Module 06 là một bài đánh giá viết được phép tra tài liệu — hãy mang theo hai file đó và không gì khác.

Trên trang này

Track your progress?

Optional. We email a link to confirm your address; progress records once you open it.

Please use your work email address, not a personal one.

Progress tracking also requires accepting the current Terms of Service in Privacy settings.

VI