BigQuery MigrationClickHouse Workshops

Troubleshooting

Simptom, sebab, dan perbaikan untuk kegagalan yang benar-benar diproduksi workshop ini -- yang benar-benar dialami persiapannya sendiri, bukan daftar generik.

Materi referensi, bukan modul untuk dikerjakan berurutan. Datang ke sini saat sesuatu berperilaku berbeda dari yang dikatakan sebuah modul sebelumnya untuk diharapkan.

Count yang kira-kira sepuluh kali terlalu besar

Simptom: count() bersama apa pun yang menyentuh event_params mengembalikan angka yang jauh lebih besar dari row count yang Anda harapkan -- kira-kira sepuluh kali terlalu besar adalah bentuk spesifik yang diproduksi dataset ini.

Sebab: arrayJoin di dalam sebuah SELECT list tidak hanya mentransformasi kolom yang dipanggilnya. Ia mengekspansi setiap baris di seluruh query menjadi satu baris per elemen array, dan setiap agregat lain di query yang sama -- termasuk count() -- melihat baris yang sudah terekspansi, bukan baris aslinya. Instinct UNNEST-di-FROM dari BigQuery menggunakan arrayJoin di sini dan mendapat kejutan ini.

-- WRONG: count() here counts event_params entries, not events
SELECT
  count()                                AS n,
  uniqExact(arrayJoin(event_params).key) AS distinct_keys
FROM bq.events_naive;
-- n: 46,095,652 -- not the event count (4,295,584), but the total number of
-- (key, value) pairs across every event's event_params array: 10.73 per event

Perbaikan: gunakan kombinator -Array sebagai gantinya. uniqExactArray, sumArray, dan yang lainnya berlaku element-wise, tanpa mengalikan baris:

-- RIGHT: pull the key array out first, no arrayJoin needed
SELECT
  count()                          AS n,
  uniqExactArray(event_params.key) AS distinct_keys
FROM bq.events_naive;
-- n: 4,295,584 -- matches the real event count

Jika sebuah count pernah terlihat lebih besar dari yang Anda harapkan, periksa apakah sebuah arrayJoin menyelip di SELECT list yang sama dulu, sebelum Anda mencurigai data-nya.

Materialized view yang tidak mengembalikan apa-apa

Simptom: Anda membuat materialized view di atas tabel yang sudah ada, query-nya, dan mendapat nol baris kembali -- padahal tabel sumbernya jelas tidak kosong.

Sebab: sebuah MATERIALIZED VIEW hanya melihat baris yang di-insert setelah ia dibuat. Ia adalah trigger pada insert baru, bukan query yang dijalankan ulang terhadap data yang sudah ada. Baris yang sudah duduk di tabel sumber saat view dibuat tidak pernah lewat melaluinya.

Perbaikan: backfill secara terpisah, sebagai langkah eksplisitnya sendiri, begitu view-nya ada:

INSERT INTO bq.funnel_agg
SELECT /* the same SELECT the view's definition uses */
FROM bq.events_tuned;

Lakukan ini sekali, tepat setelah membuat view-nya, sebelum Anda mempercayai agregat apa pun yang dibaca darinya.

CREATE TABLE gagal dengan "Sorting key contains nullable columns"

Simptom: CREATE TABLE langsung ditolak dengan error yang menyebutkan kolom nullable di sort key.

Sebab: export BigQuery membuat setiap kolom Nullable, dan sort key MergeTree tidak bisa dibangun pada kolom nullable secara default -- NULL tidak punya posisi yang didefinisikan dalam sort order.

Perbaikan: tambahkan setting yang mengizinkannya:

CREATE TABLE bq.events_naive (...)
ENGINE = MergeTree
ORDER BY event_timestamp
SETTINGS allow_nullable_key = 1;

atau, lebih baik, jangan bawa nullability itu sama sekali: beri kolom terdepan sort key Anda tipe non-nullable yang nyata dengan default yang sensible alih-alih Nullable. Setting-nya melewati error, tapi tipe non-nullable adalah perbaikan yang sebenarnya, dan itu yang dihargai baik challenge kompresi maupun sort-key.

event_date terurut dengan benar, tapi hanya karena keberuntungan

Simptom: filter range tanggal dan min()/max() di atas event_date memberi jawaban yang benar, padahal event_date adalah String, bukan Date.

Sebab: event_date adalah string dalam bentuk %Y%m%d -- "20260115". Urutan leksikografis string dan urutan kalender kebetulan sepakat untuk format yang tepat itu, sehingga perbandingan keluar dengan benar secara kebetulan, bukan karena ClickHouse memahaminya sebagai tanggal. Layout lain apa pun dari informasi yang sama -- "1/15/2026", atau locale yang menulis hari lebih dulu -- akan merusak setiap satu perbandingan ini secara diam-diam, tanpa error untuk menandainya.

Perbaikan: tidak ada yang rusak untuk diperbaiki di dataset ini, tapi jangan membangun di atas string itu seolah-olah itu tipe tanggal yang nyata. Parse dengan toDate(parseDateTime64BestEffort(event_date)) atau yang serupa sebelum mengandalkannya untuk apa pun di luar equality string, dan jangan pernah mengasumsikan tanggal string-encoded dari export BigQuery akan terurut dengan benar secara umum.

Query yang terlihat tidak lebih cepat di console

Simptom: Anda membangun ulang sebuah tabel secara khusus untuk membuat query lebih cepat, menjalankan kedua versi di SQL console, dan waktu wall-clock-nya nyaris tidak berubah.

Sebab: yang diukur jam console mencakup round trip network yang lengkap antara browser Anda dan service-nya -- puluhan hingga ratusan milidetik, setiap run -- di atas apa pun yang dilakukan query-nya sendiri. Round trip itu tidak mengecil berapa pun bagus Anda menuning query-nya, dan itu bisa lebih besar dari keseluruhan peningkatan yang Anda coba ukur.

Perbaikan: jangan percaya jam console untuk apa pun yang lebih kecil dari round trip itu. Baca read_rows langsung dari results bar console-nya sebagai gantinya -- tidak seperti jam dinding, itu tidak berubah antara run dari query yang sama, karena itu menghitung sesuatu yang benar-benar dilakukan query-nya, bukan seberapa lama round trip-nya berlangsung. Modul 05 dan 06 membahas ini secara lengkap.

ClickPipes tidak menunjukkan baris apa pun

Simptom: Anda membuat ClickPipe, statusnya running atau completed, dan tabel destination-nya kosong atau kurang dari hitungan yang diharapkan.

Sebab: hampir selalu salah satu dari dua hal. Glob path source-nya sebenarnya tidak cocok dengan object di bucket-nya -- typo di prefix-nya, * yang hilang, atau path yang mengarah satu direktori terlalu tinggi atau terlalu rendah. Atau pipe-nya belum benar-benar selesai; "started" dan "completed" adalah state yang berbeda, dan pipe yang masih melakukan ingesting akan under-report sampai selesai.

Perbaikan: periksa ulang path yang eksak terhadap yang dipakai modul 03 --

https://storage.googleapis.com/ch-workshop-bq-migration/ga4-events/*.parquet

-- dan konfirmasi status pipe-nya membaca Completed, bukan sekadar Running, sebelum Anda menghitung baris. Begitu sudah, SELECT count() FROM bq.events_naive seharusnya mengembalikan 4,295,584.

Alias yang membayangi kolom sumbernya sendiri

Simptom: sebuah query berhasil dikompilasi dan mengembalikan baris, tapi sebuah value keluar salah dengan cara yang terlihat seperti membaca kolom yang salah -- paling sering saat meratakan sebuah field bersarang.

Sebab: output alias yang memakai ulang nama kolom sumber resolve ke alias-nya, bukan kolom yang mendasarinya, di mana pun ia muncul lagi di query yang sama. Ini paling keras menggigit saat nama yang alami dan jelas untuk sebuah field yang diratakan identik dengan nama struct induknya sendiri -- traffic_source.source secara alami ingin dialiaskan traffic_source, yang juga adalah kolom tuple traffic_source top-level milik bq.events_naive sendiri. Referensikan traffic_source lagi di mana pun kemudian di query yang sama dan sekarang artinya adalah alias, bukan struct-nya.

Perbaikan: kualifikasikan setiap referensi tabel sumber dengan alias tabel, sehingga tidak ada nama yang tersisa untuk dibayangi sebuah output alias:

-- WRONG: bare traffic_source is ambiguous once aliased below
SELECT traffic_source.source AS traffic_source, ...
FROM bq.events_naive;

-- RIGHT: qualify the source reference with a table alias
SELECT n.traffic_source.source AS traffic_source, ...
FROM bq.events_naive AS n;

Ini terjadi tiga kali terpisah saat query referensi workshop ini sendiri dibangun, selalu saat meratakan schema bersarang. Kualifikasikan referensi sumber pada query apa pun yang baik membaca struct bersarang maupun menamai kolom output-nya sesuai nama struct-nya.

Mengukur sort key sebelum part-nya termerge

Simptom: Anda mengubah sort key sebuah tabel secara khusus untuk mengurangi baris yang dibaca, mengukurnya tepat setelah load data, dan peningkatannya terlihat jauh lebih kecil dari yang diharapkan -- atau tidak ada.

Sebab: satu INSERT massal mendarat sebagai beberapa part, masing-masing disortir secara independen. Value yang sama -- baris satu user, baris satu hari, apa pun yang dikelompokkan sort key Anda -- duduk di setiap part itu sampai background merge menggabungkannya, sehingga sebuah query menyentuh satu granule per part alih-alih satu granule total, berapa pun bagus sort key-nya. Ini adalah artifak transien dari bulk load yang baru, bukan steady state yang akhirnya disettle tabelnya.

Diukur dalam workshop ini tepatnya: sebuah tabel membaca 57,344 baris di 5 granule yang tersebar tepat setelah loading, dan 16,384 baris di 2 granule yang bersebelahan beberapa menit kemudian, setelah background merge ClickHouse menyusul sendiri -- tabel yang sama, query yang sama, sort key yang sama.

Perbaikan: beri tabel beberapa menit setelah loading, lalu jalankan ulang pengukurannya -- percayai angka yang belakangan itu, bukan yang Anda ukur tepat setelah insert.

Modul 05 membahas ini secara lengkap, bersama query condition cache, yang memproduksi ilusi yang terkait tapi berbeda -- sebuah baseline naive yang terlihat cepat secara artifisial karena sudah pernah melihat predikat literal yang eksak yang sedang Anda ukur waktunya. Lakukan keduanya -- tunggu part-nya termerge, dan matikan condition cache dengan SETTINGS use_query_condition_cache = 0 -- sebelum mempercayai angka read_rows apa pun yang diminta workshop ini dari Anda.

Di halaman ini

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.

ID