Topik wawancara representatif

Wawancara Rekayasa Data: Bagaimana Cara Anda Memilih Strategi Partisi dan Membuktikan Bahwa Pemangkasan Partisi Berfungsi?

DataSulit
Tim Redaksi Offer.ccDipublikasikan Diperbarui

Pertanyaan

Sebuah tabel *event* berukuran 1 PiB menambahkan 6 TiB per hari, mempertahankan data selama 180 hari, serta melayani kueri rentang waktu dan *tenant*. Bagaimana Anda memilih spesifikasi partisinya, menghindari ledakan partisi (*partition explosion*), dan membuktikan bahwa pemangkasan partisi (*partition pruning*) berfungsi?

Konteks dan Pertanyaan

Tabel event Apache Iceberg berukuran sekitar 1 PiB dan bertambah 6 TiB per hari. Tabel ini memiliki retensi 180 hari. Setiap kueri memiliki rentang event_time, biasanya satu hingga tujuh hari; 35% di antaranya juga memiliki filter kesetaraan (equality filter) pada salah satu dari 12.000 nilai tenant_id. Event dapat tiba terlambat hingga 48 jam, dan backfill data historis dapat terjadi sewaktu-waktu. Tabel tanpa partisi saat ini memindai terlalu banyak data.

Pilihlah spesifikasi partisi awal, tunjukkan mengapa alternatif umum lainnya gagal, dan jelaskan bagaimana Anda membuktikan keberhasilan pemangkasan daripada sekadar mengasumsikannya. Cakup bentuk predikat, skew, ukuran berkas, data yang terlambat (late data), evolusi partisi, peluncuran (rollout), dan pemulihan (rollback).

Semua ukuran, persentase, kardinalitas, dan periode retensi adalah asumsi wawancara. Pertanyaan ini ditujukan untuk data engineer, analytics-platform engineer, dan lakehouse engineer. Kompetensi utamanya adalah tata letak data fisik (physical data layout) dan perencanaan kueri (query planning), sehingga kategorinya adalah data.

Hal yang Dievaluasi Pewawancara

Sinyal pertama adalah penalaran berbasis beban kerja (workload-first reasoning). Kolom partisi berguna jika predikat yang umum memungkinkan mesin (engine) mengecualikan data fisik. Kardinalitas saja tidak menjadikannya kunci partisi yang baik.

Sinyal kedua adalah kedisiplinan granularitas (granularity discipline). Partisi yang terlalu kasar membaca bita yang tidak perlu; partisi yang terlalu halus atau berkardinalitas tinggi menghasilkan metadata, berkas-berkas berukuran sangat kecil (tiny files), dan penulisan yang mahal. Jawaban yang kuat memperkirakan jumlah bita dan jumlah partisi sebelum memilih suatu spesifikasi.

Sinyal ketiga adalah pembuktian. Kueri yang berisi kondisi tanggal tidak menjamin terjadinya pemangkasan. Kandidat memeriksa physical plan atau estimasi dry-run, berkas dan partisi yang direncanakan, bita yang dipindai, serta kesetaraan hasil (result equivalence).

Sinyal keempat adalah kesadaran siklus hidup (lifecycle awareness). Waktu kejadian (event time) dan waktu penyerapan (ingestion time) melayani kebutuhan yang berbeda. Data yang terlambat mengubah partisi event-time lama. Evolusi partisi juga membiarkan berkas lama tetap berada di bawah spesifikasi lama hingga ditulis ulang (rewritten).

Pertanyaan Klarifikasi Sebelum Menjawab

  • Predikat mana yang wajib dan selektif? Jika sebagian besar kueri tidak menyertakan waktu, partisi waktu tidak dapat membatasi pemindaiannya. Jika hampir setiap kueri memilih satu tenant, pembagian bucket (bucketing) berdasarkan tenant menjadi lebih bernilai.
  • Apakah bisnis memfilter berdasarkan waktu kejadian atau waktu kedatangan? Partisi event-time sesuai dengan laporan tetapi menerima penulisan data terlambat. Partisi ingestion-time menyederhanakan operasi penambahan (append), tetapi dapat menyebarkan satu hari bisnis ke beberapa partisi kedatangan.
  • Bagaimana distribusi tenant? Hash bucket tetap dapat mentoleransi kardinalitas tinggi, tetapi tenant dominan tetap dapat menimbulkan skew. Jalur khusus (dedicated path) hanya dapat dijustifikasi setelah dilakukan pengukuran.
  • Apa batasan berkas dan metadata yang dimiliki engine? Tujuannya bukanlah memaksimalkan jumlah partisi. Setiap partisi aktif harus menerima bita yang cukup untuk menghasilkan berkas yang bermanfaat.
  • Bisakah format tabel menyembunyikan transformasi dan merevolusi spesifikasinya? Jika tidak, kueri dan writer mungkin memerlukan kolom partisi turunan yang eksplisit serta rencana migrasi.

Kerangka Jawaban 30 Detik

“Saya akan memulai dari log predikat, bukan dari kardinalitas kolom. Setiap kueri memfilter event_time, jadi saya akan melakukan canary pada day(event_time). Enam TiB per hari adalah ukuran yang besar; untuk 35% kueri dengan kesetaraan tenant, saya akan menguji penambahan transformasi bucket(64, tenant_id) tetap. Ini menghasilkan paling banyak 64 grup fisik per hari, alih-alih 12.000 partisi tenant.

Saya akan menggunakan rentang event-time setengah terbuka (half-open), memeriksa plan serta berkas yang direncanakan, dan membandingkan bita yang dipindai terhadap kontrol tanpa partisi sembari memverifikasi hasil yang identik. Saya akan menolak tenant_id/hour karena dapat menghasilkan ratusan ribu grup per hari dan membebani writer. Event yang terlambat akan memperbarui partisi hari kejadian yang sesuai. Saya akan meluncurkannya per kelas beban kerja dan merevolusi spesifikasi Iceberg hanya setelah manfaat pemangkasan, ukuran berkas, biaya penulisan, dan metadata tetap berada dalam batas penerimaan.”

Pembahasan Mendalam Langkah demi Langkah

Langkah 1: Ubah Log Kueri Menjadi Matriks Predikat

Ambil sampel kueri produksi sebelum memilih tata letak. Untuk setiap kelas beban kerja, catat frekuensi, bobot latensi atau biaya, lebar rentang waktu, selektivitas tenant, operasi join, dan apakah predikat tersebut tersedia sebelum memindai tabel fakta. Memberikan bobot berdasarkan frekuensi saja dapat mengoptimalkan dasbor berbiaya murah namun mengabaikan pekerjaan harian langka yang membaca sebagian besar tabel.

Skenario ini memberikan satu invarian yang kuat: setiap kueri memiliki rentang event-time. Hal ini menjadikan event-time sebagai dimensi pemangkasan utama. Kesetaraan tenant hanya muncul pada 35% kueri, sehingga tenant tidak boleh menjadi satu-satunya dimensi partisi. Bidang pencarian bentuk bebas (free-form search), nilai metrik, atau ID event berkardinalitas tinggi tidak cocok karena tidak sesuai dengan jalur akses dominan maupun menghasilkan grup fisik yang stabil.

Langkah 2: Estimasi Ukuran dan Jumlah Partisi Kandidat

Partisi event-time harian menerima sekitar 6 TiB. Oleh karena itu, kueri tujuh hari dapat dimulai dengan sekitar 42 TiB sebelum pelewatan di tingkat berkas dan row-group (file- and row-group-level skipping). Partisi per jam menerima rata-rata sekitar 256 GiB:

text
6 TiB / 24 = 0.25 TiB = 256 GiB per hour

Keduanya cukup besar untuk mengisi berkas kolumnar (columnar files) standar. Partisi harian menghasilkan sekitar 180 grup waktu aktif; partisi per jam menghasilkan sekitar 4.320 grup. Pilihan yang lebih halus hanya dapat dibenarkan jika banyak kueri mencakup beberapa jam saja serta metadata tambahan dan write fan-out terbukti meningkatkan efisiensi biaya secara terukur.

Mempartisi secara langsung berdasarkan tenant_id dan jam memiliki batas atas yang berbahaya:

text
12,000 tenants * 24 hours = 288,000 tenant-hour groups per day

Grup yang renggang dan skew membuat jumlah realisasi menjadi lebih rendah, tetapi batas atas ini memperlihatkan potensi mode kegagalan (failure mode). Writer dapat menyentuh banyak grup per batch dan menutup berkas-berkas yang sangat kecil. Transformasi bucket(64, tenant_id) tetap membatasi dimensi tenant pada 64 grup per partisi waktu. Dengan data yang seragam, setiap bucket harian menerima sekitar 96 GiB; skew sebenarnya harus diukur.

Langkah 3: Pilih Spesifikasi Paling Sederhana yang Memenuhi Beban Kerja

Mulailah dengan day(event_time). Ini melayani setiap kueri dan memiliki beban metadata paling kecil. Kemudian lakukan tolok ukur (benchmark) terhadap alternatif ini untuk beban kerja yang dominan dengan filter tenant:

sql
PARTITIONED BY (day(event_time), bucket(64, tenant_id))

Jumlah bucket ini adalah kandidat wawancara, bukan ketentuan mutlak. Bandingkan 16, 32, 64, dan 128 menggunakan distribusi tenant yang sebenarnya, target ukuran berkas, paralelisme writer, dan konkurensi kueri. Penambahan bucket hanya membantu jika engine dapat menurunkan bucket dari predikat kesetaraan tenant dan pengurangan berkas yang direncanakan lebih besar nilainya daripada metadata dan write fan-out.

Jangan mengodekan tata letak partisi ke dalam SQL bisnis jika format tabel mendukung transformasi tersembunyi (hidden transforms). Kueri harus memfilter event_time dan tenant_id logis; metadata tabel akan memetakan predikat tersebut ke partisi fisik. Hal ini menghindari ketidakkonsistenan kolom turunan secara diam-diam dan memungkinkan spesifikasi berkembang tanpa harus menulis ulang setiap kueri konsumen.

Langkah 4: Tulis Predikat yang Dapat Dipangkas oleh Planner

Gunakan rentang setengah terbuka yang jelas dalam zona waktu bisnis yang dikonversi ke batas UTC:

sql
SELECT tenant_id, event_type, COUNT(*)
FROM analytics.events
WHERE event_time >= TIMESTAMP '2026-07-01 00:00:00 UTC'
  AND event_time <  TIMESTAMP '2026-07-08 00:00:00 UTC'
  AND tenant_id = 'tenant-42'
GROUP BY tenant_id, event_type;

Interval setengah terbuka menghindari penghitungan ganda pada batas interval saat jendela yang berdekatan dijalankan. Membungkus sumber partisi dalam fungsi yang tidak didukung, membandingkannya dengan kolom lain yang bergantung pada baris (row-dependent), menyembunyikannya di balik OR, atau melakukan casting yang tidak konsisten dapat mencegah eliminasi statis (static elimination). Transformasi persis yang didukung bergantung pada masing-masing engine, jadi verifikasilah plan alih-alih menghafal aturan satu pengoptimal (optimizer).

Untuk laporan tanggal lokal, hitung waktu mulai dan akhir instan UTC dari zona waktu yang diminta sebelum kueri dijalankan. Pengurangan 24 jam tetap adalah salah jika melewati transisi waktu musim panas (daylight saving). Timestamp event yang disimpan, transformasi partisi, dan batas pelaporan juga harus menggunakan konvensi timestamp yang terdokumentasi.

Langkah 5: Buktikan Pemangkasan dan Kebenaran Data

Bangun matriks pengujian dengan rentang satu jam, satu hari, tujuh hari, varian tenant dan non-tenant, rentang kosong, event terlambat, dan batas waktu musim panas. Untuk setiap kueri, catat:

  • logical plan dan physical plan, termasuk filter partisi atau berkas;
  • jumlah partisi dan berkas yang direncanakan;
  • estimasi dan bita aktual yang dipindai;
  • waktu perencanaan (planning time), eksekusi p50/p95, dan jumlah tugas (task count);
  • jumlah baris keluaran, agregat, dan checksum terhadap kontrol.

Snapshot tanpa partisi menjadi kontrolnya. Untuk kueri satu hari di atas 180 hari yang berukuran seragam, ekspektasi kasarnya adalah pemangkasan waktu akan mempertimbangkan sekitar 1/180 dari bita aktif, sebelum memperhitungkan metadata, skew, dan statistik berkas. Ini adalah pemeriksaan tingkat besaran (order-of-magnitude), bukan rasio mutlak. Plan yang menyebutkan filter tetapi masih memindai semua berkas dinyatakan tidak lolos pengujian.

Uji juga kontrol negatif. Hapus predikat waktu, tulis ulang menjadi ekspresi yang tidak didukung, dan gunakan rentang tenant alih-alih kesetaraan. Pemindaian harus meluas dengan cara yang dapat dijelaskan. Kontrol negatif membuktikan bahwa pengukuran dapat mendeteksi pemangkasan yang gagal berfungsi.

Langkah 6: Perhitungkan Penulisan, Data Terlambat, dan Pemeliharaan

Partisi mengubah jalur penulisan (write path). Lacak writer yang terbuka per tugas, berkas yang dibuat per commit, ukuran berkas p50/p90, latensi commit, pertumbuhan metadata, dan konflik percobaan ulang (retry conflicts). Distribusikan baris berdasarkan transformasi partisi sebelum menulis dan sesuaikan jumlah writer dari bita per grup. Pemangkasan partisi tidak dapat mengompensasi jutaan berkas kecil.

Karena laporan menggunakan event_time, event yang tiba 48 jam terlambat masuk ke dalam partisi hari kejadian historisnya. Writer dan pemadatan (compaction) harus mengizinkan pembaruan tersebut. Tentukan kapan suatu partisi menjadi dingin (cold), dan tunda proses tulis ulang yang agresif hingga jendela keterlambatan berakhir. Backfill membutuhkan rentang partisi yang dibatasi, penulisan yang idempoten, dan pemeriksaan ukuran berkas yang sama dengan penyerapan streaming.

Langkah 7: Evolusi Tanpa Penulisan Ulang Sekaligus (Big-Bang)

Dengan partisi tersembunyi Iceberg, spesifikasi baru berlaku untuk berkas baru sementara berkas lama mempertahankan spesifikasi sebelumnya. Planner dapat membaca keduanya, tetapi manfaatnya bervariasi hingga data lama yang panas atau sering dikueri ditulis ulang. Catat snapshot dasar, lakukan canary pada rentang terkini, dan perluas hanya jika kueri pada spesifikasi lama dan baru tetap memberikan hasil yang benar.

Rollback berarti menghentikan writer baru pada spesifikasi baru dan memulihkan konfigurasi penulisan sebelumnya atau snapshot tabel jika didukung. Ini tidak secara otomatis membatalkan berkas yang sudah dihapus. Pertahankan retensi snapshot dan pembersihan fisik di luar jendela canary. Tulis ulang data historis hanya jika penghematan yang terukur sebanding dengan I/O dan risiko konflik.

Kriteria penerimaan harus mencakup hasil bisnis yang identik, eliminasi partisi yang diharapkan untuk setiap kelas beban kerja, pemindaian bita atau latensi yang lebih rendah, persentil ukuran berkas yang sehat, waktu perencanaan yang terbatas, tidak ada regresi SLA penyerapan, dan pertumbuhan metadata yang stabil.

Contoh Jawaban Berkualitas Tinggi

“Saya akan mengekstrak distribusi predikat nyata terlebih dahulu. Di sini, setiap kueri memiliki rentang event-time, sedangkan hanya 35% yang memilih satu tenant, jadi waktu adalah dimensi utama. Partisi harian menampung sekitar 6 TiB dan menghasilkan 180 grup waktu aktif. Partisi per jam menampung sekitar 256 GiB tetapi menciptakan 4.320 grup waktu aktif; saya hanya akan menggunakannya jika kueri dengan rentang jam yang sempit membenarkan kebutuhan metadata ekstra tersebut.

Kandidat dasar canary saya adalah day(event_time). Untuk kueri yang dominan memfilter tenant, saya akan menguji day(event_time), bucket(64, tenant_id). Enam puluh empat hanyalah sebuah kandidat: opsi ini membatasi fan-out tenant dan memberikan sekitar 96 GiB per bucket harian di bawah distribusi seragam. Saya akan menolak partisi langsung tenant-jam karena batas atasnya adalah 288.000 grup per hari, yang berisiko menciptakan berkas kecil dan masalah writer fan-out.

Kueri menggunakan rentang setengah terbuka UTC dan kesetaraan tenant. Saya akan memeriksa physical plan, berkas yang direncanakan, dan bita yang dipindai, lalu membandingkan hasil dan checksum dengan snapshot tanpa partisi. Matriks pengujian mencakup kasus satu jam, satu hari, tujuh hari, kosong, event terlambat, tenant, non-tenant, dan batas waktu musim panas. Saya juga akan menjalankan kontrol negatif yang menghapus atau mengaburkan predikat waktu.

Event yang terlambat akan menulis ke partisi hari kejadiannya, sehingga pemadatan ditunda melampaui jendela keterlambatan 48 jam. Selama canary, saya memantau persentil berkas, writer per tugas, waktu perencanaan p95, metadata, write lag, dan konflik. Jika evolusi partisi Iceberg tersedia, berkas baru akan mengadopsi spesifikasi baru sementara berkas lama tetap dapat dibaca. Saya hanya menulis ulang data lama jika penghematan kueri terukur melebihi biaya penulisan ulang. Adanya perbedaan kebenaran hasil, pemindaian seluruh berkas, lonjakan berkas kecil, atau regresi SLA penyerapan akan langsung menghentikan peluncuran.”

Kesalahan Umum

  • Memilih kolom dengan kardinalitas tertinggi → Ini menciptakan grup yang renggang dan berkas-berkas kecil tanpa melayani predikat dominan → Mulailah dari predikat kueri berbobot dan batasi grup fisik.
  • Mempartisi berdasarkan tenant dan jam → Skenario ini memungkinkan 288.000 grup per hari → Gunakan waktu terlebih dahulu dan uji tolok ukur transformasi bucket tetap.
  • Melihat filter tanggal lalu mengasumsikan pemangkasan terjadi → Ekspresi yang tidak didukung masih dapat memindai setiap berkas → Periksa physical plan, berkas yang direncanakan, dan bita.
  • Hanya memvalidasi latensi → Cache, konkurensi, atau komputasi ekstra dapat meniru peningkatan performa → Gunakan bita pemindaian, bukti plan, kontrol negatif, dan sumber daya yang setara.
  • Menggunakan waktu penyerapan untuk laporan event-time tanpa analisis → Event terlambat untuk satu hari bisnis akan tersebar di berbagai partisi kedatangan → Selaraskan dimensi fisik dengan semantik waktu kueri.
  • Mengabaikan ukuran berkas → Partisi yang terlalu halus membebani writer dan mengalihkan biaya dari pemindaian ke perencanaan → Lacak bita per grup, writer fan-out, dan persentil berkas.
  • Menulis ulang seluruh 1 PiB data secara instan → Biaya, konflik, dan risiko rollback menjadi berlebihan → Gunakan canary pada rentang terkini dan tulis ulang hanya riwayat data yang terjustifikasi.
  • Mengasumsikan evolusi menulis ulang berkas lama secara otomatis → Beberapa spesifikasi dapat hidup berdampingan → Uji perencanaan lintas spesifikasi (mixed-spec) dan jadwalkan penulisan ulang selektif.

Pertanyaan Lanjutan dan Tanggapan

Lanjutan 1: Apa yang berubah jika 95% kueri memfilter satu tenant?

Pembagian bucket berdasarkan tenant memiliki bobot lebih besar. Hitung ulang jumlah bucket dari distribusi tenant dan konkurensi kueri, lalu bandingkan skema waktu saja, waktu-plus-bucket, dan kemungkinan tata letak terisolasi-tenant untuk tenant yang dominan. Partisi langsung 12.000 arah tetap memerlukan bukti bahwa setiap grup menghasilkan ukuran berkas yang sehat dan metadata yang dapat dikelola.

Lanjutan 2: Mengapa tidak mempartisi per jam jika 256 GiB per jam masih tergolong besar?

Partisi per jam mungkin tepat jika sebagian besar kueri mencakup hitungan menit atau jam dan pemangkasan tujuh partisi harian dinilai terlalu kasar. Namun, opsi ini menghasilkan 24 kali lebih banyak grup waktu dan lebih banyak write fan-out. Uji performa perencanaan, bita, ukuran berkas, dan biaya penyerapan untuk distribusi rentang aktual sebelum menyetujui kompromi tersebut.

Lanjutan 3: Bagaimana jika kueri memfilter hari kalender lokal?

Konversikan awal hari lokal yang diminta dan awal hari berikutnya menjadi waktu instan UTC, lalu gunakan rentang setengah terbuka. Ini menangani hari-hari transisi waktu musim panas yang berdurasi 23 atau 25 jam. Verifikasikan bahwa transformasi partisi dan konvensi timestamp yang disimpan memungkinkan engine menurunkan hari fisik yang relevan.

Lanjutan 4: Bagaimana jika plan menunjukkan pemangkasan tetapi bita tidak berkurang?

Periksa apakah partisi yang dipilih berisi sebagian besar data tabel akibat skew, apakah berkas lama menggunakan spesifikasi lain, apakah filter sisa (residual filters) tidak dapat menggunakan statistik berkas, dan apakah bita yang dilaporkan mencakup tahapan yang tidak terkait. Bandingkan ID berkas yang direncanakan dan kontrol tanpa partisi; label dalam plan saja bukanlah bukti yang cukup.

Lanjutan 5: Bagaimana cara mengubah partisi harian ke per jam dengan aman?

Terapkan canary spesifikasi baru pada berkas baru, pertahankan snapshot sebelumnya, dan kueri rentang yang melintasi tata letak lama dan baru. Ukur kebenaran data, waktu perencanaan, ukuran berkas, dan biaya penulisan. Tulis ulang hanya rentang historis yang manfaat terukurnya dapat menutup biaya I/O, dan tunda pembersihan fisik hingga jendela rollback dan retensi selesai.

Sumber publik

Pertanyaan terkait