Konteks dan Cakupan
Anda bertanggung jawab atas tugas analitik lokal. DuckDB membaca file Parquet dan menjalankan multi-table JOIN, operasi GROUP BY, dan window function. Seiring bertambahnya data, tugas tersebut mengalami Out of Memory atau membuat terlalu banyak file sementara dan mengalami timeout. Jelaskan urutan diagnosis, perubahan parameter, penulisan ulang SQL, dan rencana verifikasi Anda.
Skenario ini cocok untuk wawancara data-engineering, analytics-engineering, dan embedded OLAP. Jawabannya harus berbasis bukti; "menambah memori" atau "membeli mesin yang lebih besar" bukanlah sebuah diagnosis.
Apa yang Dinilai oleh Pewawancara
- Apakah Anda membedakan eksekusi streaming dari operator yang mempertahankan state besar.
- Apakah Anda menggunakan execution plan, profil runtime, dan sinyal memori daripada sekadar menebak.
- Apakah Anda memahami thread, batas memori, direktori spill, dan pemeliharaan urutan penyisipan (insertion-order preservation).
- Apakah Anda memeriksa kapasitas disk sementara, izin akses (permissions), tipe data, indeks, dan ledakan hasil JOIN.
- Apakah kebenaran (correctness), reproduktifitas, dan performa regresi menutup siklus pengoptimalan.
Pertanyaan Klarifikasi Sebelum Menjawab
- Apakah kegagalan terjadi saat scanning, JOIN, agregasi, pengurutan, atau window? Apakah ini galat DuckDB atau penghentian paksa oleh sistem operasi (OS kill)?
- Berapa versi DuckDB, jumlah thread,
memory_limit, jalur direktori sementara, dan ruang disk yang tersedia? - Apakah input berupa Parquet/CSV, apa saja tipe kolom dan tata letak partisi, dan apakah predikat dapat didorong ke bawah (predicate pushdown)?
- Apakah kueri mengandung GROUP BY ber-kardinalitas tinggi, DISTINCT eksak, JOIN lebar, ORDER BY, window,
list/string_agg, atau PIVOT? - Bisakah hasilnya diproses dalam batch, di-pra-agregasi, diaproksimasi, atau dikembalikan tanpa mempertahankan urutan input?
Kerangka Jawaban 30 Detik
Saya pertama-tama mengidentifikasi operator dan sumber daya yang habis, kemudian menetapkan baseline dengan EXPLAIN ANALYZE, snapshot memori, dan metrik direktori sementara. Jika agregat ber-kardinalitas tinggi, JOIN, pengurutan, atau window menciptakan state pemblokir, saya mengurangi baris dan kolom yang dipindai serta memperbaiki kondisi filter atau join sebelum menurunkan konkurensi, menetapkan batas memori yang aman, dan memvalidasi direktori spill. Terakhir, saya membandingkan jumlah baris, keunikan kunci, agregat, dan latensi pada input tetap maupun penuh agar pengoptimalan lebih cepat dan aman secara semantik.
Pembahasan Mendalam Langkah demi Langkah
1. Pisahkan kegagalan memori dari kegagalan disk sementara
Catat teks galat, alasan penghentian proses, peak RSS, versi DuckDB, jumlah thread, dan fingerprint kueri. DuckDB mencadangkan sebagian memori yang tersedia sebagai batas, tetapi OOM sistem operasi, batasan container, direktori sementara yang tidak dapat ditulisi, atau disk penuh dapat terlihat serupa. Verifikasi batas cgroup/container, kapasitas, dan izin akses sebelum mengubah SQL.
2. Temukan operator pemblokir di dalam plan
Gunakan EXPLAIN untuk memeriksa urutan join dan predicate pushdown, kemudian EXPLAIN ANALYZE untuk melihat baris aktual, timing, dan state saat runtime. Scan biasanya memproses chunk, sedangkan GROUP BY, JOIN, ORDER BY, window, dan DISTINCT eksak menahan hash table, sort buffer, atau frame. Jika join key yang salah melipatgandakan baris, perbaiki semantiknya sebelum mendiskusikan memori.
3. Kurangi working set sebelum menyesuaikan sumber daya
Baca hanya kolom yang diperlukan, tambahkan filter partisi dan waktu sejak awal, dan hindari mematerialisasi seluruh relasi lebar di dalam subquery. Pra-agregasi fakta mahal yang dapat digunakan kembali berdasarkan partisi, dan bagi JOIN many-to-many yang jelas menjadi beberapa langkah dengan pemeriksaan keunikan. Jangan mengganti statistik eksak ber-kardinalitas tinggi dengan aproksimasi tanpa batas toleransi kesalahan (error budget) yang eksplisit.
4. Atur dan verifikasi thread, memori, dan spilling
Jumlah thread yang lebih banyak dapat membuat beberapa operator menahan state secara bersamaan, jadi kurangi threads pada host dengan sumber daya terbatas. Jaga memory_limit di bawah alokasi container untuk menyisakan ruang bagi sistem; menaikkannya secara membabi buta dapat mengubah galat DuckDB menjadi OS kill. Untuk spilling, gunakan disk lokal yang cepat, dapat ditulisi, dan memiliki kapasitas yang diketahui. Eksperimen terkontrol dapat menggunakan:
SET threads = 4;
SET memory_limit = '4GB';
SET temp_directory = '/var/tmp/duckdb_swap';
SET preserve_insertion_order = false;
EXPLAIN ANALYZE
SELECT customer_id, date_trunc('day', event_time) AS day, sum(amount) AS total
FROM read_parquet('events/*.parquet')
WHERE event_time >= DATE '2026-01-01'
GROUP BY customer_id, day;Nonaktifkan preserve_insertion_order hanya jika hasil bisnis tidak bergantung pada urutan input. Indeks dan beberapa state perantara tidak selalu diatur oleh buffer manager, sehingga memory_limit bukanlah batas keras yang universal.
5. Identifikasi perilaku spill dan batas operator
Spilling mendukung banyak beban kerja GROUP BY, JOIN, pengurutan, dan window berskala besar, tetapi ini menambah I/O. Rangkaian operator pemblokir, agregat list berukuran sangat besar, string_agg, beberapa agregat holistik, dan PIVOT mungkin masih memerlukan state besar yang tidak dapat dipecah. Jika direktori sementara membengkak secara tidak terduga, periksa temp_directory, max_temp_directory_size, throughput disk, dan proses pembersihan. Jika spilling tidak dapat membantu, kembalilah ke pemrosesan batch atau tulis ulang struktur SQL.
6. Tutup dengan regresi hasil dan performa
Gunakan snapshot input tetap untuk membandingkan total baris, kumpulan primary key, distribusi NULL, jumlah grup, checksum, dan detail sampel sebelum dan sesudah perubahan. Catat peak memori, byte sementara, byte yang dipindai, waktu proses (runtime), dan tingkat kegagalan. Uji batas tanggal, partisi kosong, kunci duplikat, dan kardinalitas ekstrem secara terpisah.
Contoh Jawaban Berkualitas Tinggi
Saya mengklasifikasikan insiden ini sebagai state operator, kapasitas konfigurasi, atau lingkungan eksternal. Pertama, saya mengamankan bukti versi, kueri, snapshot input, memori container, dan disk sementara, lalu menggunakan EXPLAIN ANALYZE untuk menemukan titik lonjakan tertinggi. Untuk GROUP BY ber-kardinalitas tinggi, JOIN many-to-many yang salah, pengurutan, atau window, saya memeriksa kardinalitas dan predicate pushdown, mengurangi kolom dan baris, serta melakukan pra-agregasi bila sesuai; saya tidak menyembunyikan ledakan join hanya dengan menaikkan memory_limit.
Selanjutnya, saya menurunkan konkurensi thread ke tingkat aman yang terukur, menyisakan ruang sistem di bawah memory_limit, dan menempatkan temp_directory pada disk dengan kapasitas dan izin akses yang jelas. Saya menonaktifkan preserve_insertion_order hanya jika urutan tidak bersifat kontraktual. Saya mengukur peak memori, byte spill, dan runtime, serta memastikan penggunaan sementara tetap berada dalam kuota. Untuk state list, string yang sangat besar, atau PIVOT yang tidak dapat dipecah secara efektif, saya menggunakan hasil bertahap atau mempertimbangkan kembali bentuk kueri.
Terakhir, saya membandingkan jumlah baris, keunikan kunci, checksum agregat, partisi batas, dan perilaku NULL pada data tetap dan penuh sebelum rilis. Ini membuktikan bahwa OOM telah teratasi dan semantik hasil tetap terjaga.
Kesalahan Umum
Hanya menaikkan memory_limit
Tanpa memeriksa batas container, ruang sistem, dan memori yang tidak dikelola oleh buffer, kegagalan dapat berpindah dari DuckDB ke sistem operasi.
Mengasumsikan setiap operator dapat melakukan spill
Konfirmasikan operator dan versi spesifiknya. Beberapa state list, string, agregat holistik, dan PIVOT masih membutuhkan memori yang tidak dapat dipecah.
Mengabaikan kardinalitas JOIN dan predicate pushdown
Kunci non-unik atau filter yang terlambat dapat menghasilkan data perantara yang berkali-kali lipat lebih besar; pengaturan konfigurasi tidak dapat memperbaiki bentuk kueri yang salah.
Memeriksa keberhasilan tanpa uji regresi hasil
Mengubah pemeliharaan urutan, memecah agregat, atau menggunakan fungsi aproksimasi dapat mengubah semantik. Bandingkan input tetap dan lakukan validasi bisnis.
Pertanyaan Lanjutan dan Tanggapan
Pertanyaan Lanjutan 1: Mengapa thread yang lebih sedikit dapat membantu?
Operator yang berjalan bersamaan dapat menahan state dan buffer pada saat yang sama. Thread yang lebih sedikit menurunkan lonjakan penggunaan memori tetapi biasanya mengurangi throughput, jadi pilihlah berdasarkan kurva memori dan waktu penyelesaian yang terukur.
Pertanyaan Lanjutan 2: Mengapa OOM dapat berlanjut padahal disk sementara masih memiliki ruang?
Tidak semua state dapat dipartisi dan dialihkan ke disk (spill). Agregat yang tidak dapat dipecah, state join yang terlalu besar, atau izin akses dan kuota direktori masih dapat menyebabkan kegagalan; gabungkan analisis plan, batas limit, dan log.
Pertanyaan Lanjutan 3: Kapan pemeliharaan urutan penyisipan boleh dinonaktifkan?
Hanya jika hasil dan sistem hilir tidak memperlakukan urutan input sebagai sebuah kontrak. Lakukan uji regresi pada kunci duplikat, pengurutan, dan perilaku LIMIT sesudahnya.
Pertanyaan Lanjutan 4: Bagaimana Anda membuktikan bahwa pengoptimalan tetap menjaga kebenaran hasil?
Jalankan snapshot input yang sama dan bandingkan jumlah baris, kumpulan primary key, jumlah grup, checksum numerik, distribusi NULL, dan partisi batas. Untuk agregat aproksimasi, nyatakan batas toleransi kesalahan dan dapatkan persetujuan bisnis.