Ilustrasi dihasilkan dengan AI. Dalam ekosistem data modern, kebutuhan untuk mengeksekusi kueri analitik cepat (OLAP) pada ...
Dalam ekosistem data modern, kebutuhan untuk mengeksekusi kueri analitik cepat (OLAP) pada berkas lokal sering kali berbenturan dengan batasan sistem. Solusi tradisional seperti database klien-server (PostgreSQL/MySQL) memerlukan overhead migrasi dan pemuatan data, sedangkan framework terdistribusi seperti Apache Spark membawa kompleksitas infrastruktur cluster yang berat untuk mesin pengembang tunggal. DuckDB hadir sebagai mesin database relasional OLAP berbasis columnar in-process yang dirancang khusus untuk memproses data analitis langsung di lingkungan lokal tanpa ketergantungan server terpisah.
Sorotan Utama Arsitektur DuckDB
- In-Process Execution: Berjalan di dalam proses aplikasi pemanggil tanpa soket jaringan atau serialisasi IPC antarmesin.
- Vectorized Engine: Memproses data dalam potongan blok kolom (vektor) memori CPU secara efisien dan memanfaatkan instruksi SIMD pada operasi serta mikroarsitektur CPU yang mendukung.
- Pembaca Parquet Terintegrasi: Mengakses metadata berkas Parquet secara efisien, menerapkan filter pushdown dan projection pushdown langsung dari format kolom.
- Manajemen Memori Terkendali: Mendukung disk spilling ke direktori temporer pada operator analitik tertentu yang mendukung (seperti sorting, hash join, dan hash aggregation tertentu) ketika alokasi memori buffer terlampaui.
1. Karakteristik Arsitektur: OLAP Vektorisasi Tanpa Overhead Server
Secara konseptual, DuckDB menempati posisi yang sebanding dengan SQLite di ranah transaksional (OLTP), namun dioptimalkan penuh untuk beban kerja analitis (OLAP). Database relasional konvensional umumnya mengadopsi model pemrosesan tuple-at-a-time (model iterator Volcano), di mana setiap tuple berpindah melalui hierarki operator kueri satu per satu. Pendekatan ini menimbulkan biaya overhead pemanggilan fungsi (function call overhead) yang signifikan dan buruk dalam pemanfaatan cache CPU pada data analitik berukuran jutaan baris.
DuckDB mengimplementasikan model eksekusi vectorized engine (terinspirasi dari VectorWise). Kueri dijalankan pada potongan kolom (data chunks) berukuran tetap (biasanya 2.048 elemen per vektor kolom). Model ini memaksimalkan efisiensi instruksi CPU modern, meminimalkan branch mispredictions, dan membuka peluang pemanfaatan instruksi SIMD (Single Instruction, Multiple Data) pada operasi serta arsitektur prosesor yang mendukungnya (bukan pada seluruh operasi kueri secara seragam).
| Karakteristik | Sistem OLTP (SQLite / PostgreSQL) | Sistem OLAP Terdistribusi (Spark) | DuckDB (Embedded OLAP) |
|---|---|---|---|
| Tata Letak Penyimpanan | Row-oriented (B-Tree/Heap) | Columnar (Parquet/ORC) | Columnar (DuckDB Native / Parquet) |
| Model Eksekusi | Tuple-at-a-time (Volcano) | Distributed JVM / Code-gen | Vectorized chunk-based (SIMD pada operasi yang mendukung) |
| Kebutuhan Infrastruktur | Lokal / Daemon Klien-Server | Cluster JVM, Manager Node, Worker | Single binary / Tanpa konfigurasi cluster |
| Latensi Permulaan (Cold Start) | Rendah (ringan / in-process) | Tinggi (alokasi cluster / inisialisasi worker) | Rendah (in-process tanpa overhead boot cluster) |
2. Instalasi CLI & Persiapan Lingkungan
DuckDB didistribusikan sebagai berkas biner mandiri (self-contained executable) tanpa dependensi dinamis eksternal. Di lingkungan Linux, biner dapat diunduh langsung dan ditempatkan ke path sistem atau folder lokal.
Langkah Persiapan Terminal (Bash):
Untuk menjaga integritas berkas dan mencegah risiko menimpa dataset atau biner yang sudah ada sebelumnya, gunakan utilitas mktemp -d guna mengalokasikan direktori kerja unik dan terisolasi. Penanganan galat disertakan secara eksplisit (fail closed) sehingga unduhan tidak akan dijalankan bila direktori gagal dialokasikan. Cetak dan catat path direktori kerja pada terminal pertama ini. Penting untuk mempertahankan direktori kerja aktif (current working directory / cwd) yang sama di seluruh blok perintah shell, eksekusi CLI SQL, maupun skrip Python karena variabel shell $WORK_DIR dan status cwd tidak diwariskan ke sesi atau jendela terminal baru:
# Alokasikan direktori kerja unik terisolasi via mktemp -d dan cek kegagalan alokasi
WORK_DIR=$(mktemp -d "${TMPDIR:-/tmp}/duckdb_lab_XXXXXX") || { echo "Gagal membuat direktori temporer unik" >&2; exit 1; }
cd "$WORK_DIR" || { echo "Gagal berpindah ke direktori kerja" >&2; exit 1; }
# Cetak dan catat path direktori kerja aktif (WORK_DIR tidak diwariskan ke sesi/terminal baru)
pwd
echo "Direktori kerja aktif: $WORK_DIR"
# Catatan Keamanan: Jangan gunakan perintah penghapusan berkas (rm).
# Dengan direktori unik mktemp -d, seluruh latihan berjalan di lingkungan baru tanpa risiko menimpa data lama.
# Unduh rilis biner DuckDB CLI (contoh arsitektur Linux x86_64)
curl -fsSL https://github.com/duckdb/duckdb/releases/download/v1.1.3/duckdb_cli-linux-amd64.zip -o duckdb.zip
# Ekstrak biner menggunakan Python zipfile (jika utilitas unzip belum terpasang)
python3 -c "import zipfile; zipfile.ZipFile('duckdb.zip').extractall('.')"
chmod +x duckdb
# Verifikasi versi biner yang terpasang
./duckdb --version
Menghasilkan Dataset Uji Akses Server
Untuk menguji kemampuan performa DuckDB secara realistis, kita membuat dataset sintetis berupa log akses server sebanyak 100.000 baris. Perintah CLI berikut menginisialisasi random seed melalui fungsi setseed(0.42) sebelum menghasilkan baris data guna membantu menjaga distribusi angka pseudorandom agar relatif konsisten selama pengujian. Kendati demikian, determinisme ini perlu dipahami secara realistis: dalam arsitektur analitik multi-threaded seperti DuckDB, eksekusi paralel antar-thread dan pemrosesan chunk dapat menimbulkan variasi urutan evaluasi baris jika jumlah thread atau topologi eksekusi berubah. Untuk pengujian yang mewajibkan reproduktifitas deterministik mutlak bit-for-bit, jalankan proses pada satu thread tunggal (SET threads = 1;) disertai pengurutan eksplisit.
# Menjalankan generasi dataset sintetis dengan random seed deterministik
./duckdb -c "
SELECT setseed(0.42);
COPY (
SELECT
TIMESTAMP '2026-11-01 00:00:00' + INTERVAL (i * 12) SECOND AS timestamp,
md5(concat('client_id_', (i % 250)::VARCHAR)) AS client_hash,
CASE (i % 5)
WHEN 0 THEN '/api/v1/auth/login'
WHEN 1 THEN '/api/v1/data/metrics'
WHEN 2 THEN '/api/v1/reports/export'
WHEN 3 THEN '/api/v1/health'
ELSE '/api/v1/checkout/process'
END AS endpoint,
CASE
WHEN (i % 31 = 0) THEN 500
WHEN (i % 17 = 0) THEN 404
ELSE 200
END AS status_code,
round(15.0 + (random() * 280.0), 2) AS latency_ms,
floor(256 + (random() * 16384))::BIGINT AS bytes_sent
FROM range(1, 100001) t(i)
) TO 'server_access_logs.csv' (HEADER, DELIMITER ',');
"
3. Analisis CSV Berkinerja Tinggi: Sniffer vs Skema Eksplisit
Secara default, DuckDB memiliki fitur CSV Auto-Detection Sniffer yang secara otomatis memindai sampel data, menentukan pemisah kolom (delimiter), format tanggal, dan tipe data dari setiap kolom tanpa konfigurasi manual.
3.1 Kueri Langsung pada File CSV
Anda dapat mengeksekusi kueri SQL langsung ke path berkas seolah-olah berkas tersebut adalah tabel di dalam database:
-- Menjalankan inspeksi 5 baris pertama langsung dari file CSV
SELECT
timestamp,
endpoint,
status_code,
latency_ms
FROM 'server_access_logs.csv'
LIMIT 5;
3.2 Menentukan Skema Eksplisit dengan read_csv()
Meskipun sniffer otomatis sangat praktis untuk eksplorasi interaktif, pada alur kerja analitik produksi disarankan menentukan skema tipe data eksplisit melalui fungsi tabel read_csv(). Ketika parameter columns = {...} didefinisikan, DuckDB secara otomatis menonaktifkan deteksi skema dan tipe kolom (mengabaikan auto_detect = true khusus untuk penetapan skema kolom). Hal ini menghilangkan overhead waktu pemindaian awal (sniffer sampling) dan memastikan tipe data sesuai kontrak skema. Kendati demikian, deteksi parameter pemisah (delimiter) dan header tetap dapat beroperasi dalam mode auto-detect kecuali seluruh parameter didefinisikan lengkap atau auto_detect = false disetel secara eksplisit.
Catatan Kritis: Deteksi CSV Malformed Bergantung pada Proyeksi Kolom dan Opsi Parser
Mendefinisikan parameter columns = {...} (dan mematikan auto_detect untuk skema) tidak secara otomatis menjamin baris data yang cacat atau malformed (misalnya nilai non-numerik pada kolom numerik atau jumlah pemisah tidak konsisten) langsung tertolak atau lolos kueri. Perilaku deteksi dan kegagalan DuckDB sangat ditentukan oleh dua faktor utama:
- Projection Pushdown (Kolom yang Diminta): DuckDB hanya mem-parsing dan memvalidasi tipe data kolom yang secara aktif diminta pada klausa
SELECTatau filterWHERE. Apabila baris data yang malformed berada pada kolom yang tidak diproyeksikan (diabaikan), kueri sering kali tetap berhasil tanpa memicuConversion Error. Sebaliknya, begitu kolom yang cacat dimasukkan ke dalam proyeksi kueri, eksekusi akan langsung dihentikan oleh parser/konverter tipe data. - Opsi Parser CSV: Toleransi terhadap baris cacat dikendalikan oleh konfigurasi eksplisit seperti
ignore_errors = true(melewati baris yang gagal dikonversi),null_padding = true(mengisi kolom yang hilang dengan nilai NULL jika jumlah kolom lebih sedikit dari skema), atau opsirejects_table(menyimpan baris-baris yang ditolak ke tabel temporer untuk kebutuhan audit dan investigasi data kotor).
Tanpa opsi toleransi di atas, kueri yang memproyeksikan kolom cacat akan berhenti seketika dengan Conversion Error atau Parser Error. Perhatikan pula bahwa untuk mem-parsing kolom bertipe TIMESTAMP, DuckDB menggunakan parameter timestampformat, berbeda dari dateformat yang khusus menangani kolom bertipe DATE.
-- Pembacaan CSV dengan deklarasi tipe kolom ketat dan konfigurasi penanganan error
SELECT
timestamp,
client_hash,
endpoint,
status_code,
latency_ms,
bytes_sent
FROM read_csv('server_access_logs.csv',
header = true,
delim = ',',
columns = {
'timestamp': 'TIMESTAMP',
'client_hash': 'VARCHAR',
'endpoint': 'VARCHAR',
'status_code': 'INTEGER',
'latency_ms': 'DOUBLE',
'bytes_sent': 'BIGINT'
},
timestampformat = '%Y-%m-%d %H:%M:%S',
nullstr = ['NA', 'null', ''],
ignore_errors = false
)
WHERE status_code = 500
LIMIT 5;
3.3 Agregasi Statistik Kompleks dan Kalkulasi Persentil
DuckDB menyediakan fungsi analitik tingkat lanjut yang dihitung secara efisien dalam satu kali pemindaian data (single pass), seperti fungsi persentil kontinu quantile_cont():
-- Agregasi metrik performa API per endpoint
SELECT
endpoint,
count(*) AS total_requests,
round(avg(latency_ms), 2) AS avg_latency_ms,
round(quantile_cont(latency_ms, 0.95), 2) AS p95_latency_ms,
round(max(latency_ms), 2) AS max_latency_ms,
round(sum(CASE WHEN status_code >= 500 THEN 1 ELSE 0 END) * 100.0 / count(*), 2) AS error_rate_pct,
round(sum(bytes_sent) / (1024.0 * 1024.0), 2) AS total_mb_transferred
FROM read_csv('server_access_logs.csv')
GROUP BY endpoint
ORDER BY total_requests DESC;
4. Transformasi dan Analisis Parquet
Format Parquet merupakan standar de-facto penyimpanan data analitis modern. Parquet mengorganisir data dalam format kolom (columnar layout) dengan kompresi bawaan per blok, statistik minimum/maksimum per row group, dan metadata hierarkis.
4.1 Konversi CSV ke Parquet dengan Kompresi ZSTD
Dengan DuckDB, konversi berkas CSV menjadi Parquet dapat dilakukan melalui satu perintah COPY. Algoritma kompresi seperti ZSTD memberikan efisiensi pemadatan tinggi dengan kecepatan dekompresi analitis yang optimal.
-- Melakukan konversi dan kompresi kolom dari CSV ke Parquet
COPY (
SELECT * FROM read_csv('server_access_logs.csv',
header = true,
columns = {
'timestamp': 'TIMESTAMP',
'client_hash': 'VARCHAR',
'endpoint': 'VARCHAR',
'status_code': 'INTEGER',
'latency_ms': 'DOUBLE',
'bytes_sent': 'BIGINT'
}
)
) TO 'server_access_logs.parquet' (FORMAT PARQUET, COMPRESSION ZSTD);
Karakteristik Efisiensi Format Parquet: Format Parquet menerapkan kompresi berbasis kolom (seperti ZSTD, Snappy, atau dictionary encoding) yang memanfaatkan tingginya redundansi data pada kolom seragam. Struktur biner ini mengeliminasi pemborosan pemisah teks CSV mentah sekaligus mempercepat pembacaan analitik. Rasio kompresi riil pada data produksi sangat bervariasi bergantung pada tipe data, entropi teks, dan kardinalitas kolom.
4.2 Filter Pushdown dan Projection Pushdown
Keunggulan terbesar pembaca Parquet pada DuckDB adalah teknik pushdown optimization:
- Projection Pushdown: Mesin hanya membaca kolom yang tertera di klausa
SELECTdanWHEREdari disk, mengabaikan kolom lainnya sama sekali. - Filter Pushdown: Menggunakan metadata statistik min/max di header Row Group Parquet untuk melewati pemindaian blok baris yang tidak memenuhi kondisi klausa filter.
-- Hanya kolom 'endpoint', 'latency_ms', dan 'status_code' yang dibaca dari disk
SELECT
endpoint,
latency_ms
FROM read_parquet('server_access_logs.parquet')
WHERE status_code = 500
AND latency_ms > 200.0
LIMIT 5;
4.3 Pembacaan Multi-File Terpisah Menggunakan Globbing
Dalam pipeline analitik data log, data sering kali dipecah menjadi beberapa berkas per periode atau per kategori. DuckDB mendukung pemindaian banyak berkas sekaligus menggunakan pola globbing (wildcard) maupun daftar berkas eksplisit.
Langkah Prasyarat: Menyiapkan Direktori Latihan Baru yang Terisolasi
Sebelum mengeksekusi kueri berbasis wildcard (globbing), siapkan direktori latihan baru menggunakan perintah mkdir tanpa flag -p (misalnya mkdir data/logs_split_v2). Perintah mkdir -p tidak menjamin direktori dalam keadaan kosong karena akan tetap sukses tanpa peringatan jika direktori sudah ada sebelumnya. Dengan mkdir tanpa flag -p, pembuatan direktori sengaja digagalkan oleh sistem (File exists) apabila folder target sudah pernah dibuat, sehingga menjamin direktori benar-benar bersih dari sisa berkas latihan sebelumnya.
Koreksi Kritis Terhadap Eksekusi Shell (Fail-Closed): Jika perintah mkdir dan ./duckdb diletakkan pada baris terpisah, pengguna yang menyalin (copy-paste) seluruh blok perintah sekaligus ke shell akan mengalami kegagalan pada mkdir, namun shell tetap melanjutkan eksekusi baris berikutnya sehingga ./duckdb tetap berjalan dan dapat menimpa data yang ada. Oleh karena itu, gunakan rangkaian fail-closed menggunakan operator &&. Selain itu, pastikan direktori induk data sudah ada menggunakan mkdir -p data (perintah ini aman dan tidak akan menimpa data lama di dalam folder data). Apabila pembuatan direktori target gagal karena sudah ada, rangkaian && langsung membatalkan eksekusi sehingga DuckDB tidak akan dijalankan.
Apabila perintah gagal karena direktori sudah ada, pilihlah nama direktori baru (misalnya data/logs_split_v3) tanpa menghapus berkas apa pun (hindari perintah penghapusan berkas agar data riil tidak hilang). Jika Anda menggunakan nama baru, pastikan seluruh path pada perintah ekspor COPY TO dan kueri read_parquet disesuaikan secara konsisten mengikuti nama direktori tersebut:
# 1. Pastikan direktori induk 'data' tersedia secara aman tanpa menimpa data lama
# 2. Rangkaian fail-closed: buat direktori latihan baru tanpa -p dan sambungkan dengan operator &&
# Jika 'data/logs_split_v2' sudah ada, mkdir gagal (File exists) dan operator && menghentikan eksekusi sehingga ./duckdb TIDAK dijalankan
mkdir -p data && mkdir data/logs_split_v2 && ./duckdb -c "
COPY (
SELECT * FROM read_parquet('server_access_logs.parquet') WHERE endpoint = '/api/v1/auth/login'
) TO 'data/logs_split_v2/logs_auth.parquet' (FORMAT PARQUET, COMPRESSION ZSTD);
COPY (
SELECT * FROM read_parquet('server_access_logs.parquet') WHERE endpoint = '/api/v1/checkout/process'
) TO 'data/logs_split_v2/logs_checkout.parquet' (FORMAT PARQUET, COMPRESSION ZSTD);
"
Setelah berkas partisi berhasil dibuat, jalankan kueri SQL. Parameter filename = true menyertakan nama berkas asal sebagai kolom virtual. Anda dapat menggunakan pola wildcard pada direktori baru tersebut, atau mendaftarkan daftar dua berkas secara eksplisit:
-- Pendekatan 1: Membaca berkas via pola globbing pada direktori latihan bersih
SELECT
filename,
count(*) AS total_records,
round(avg(latency_ms), 2) AS avg_latency
FROM read_parquet('data/logs_split_v2/*.parquet', filename = true)
GROUP BY filename;
-- Pendekatan 2: Menyebutkan daftar dua berkas secara eksplisit
SELECT
filename,
count(*) AS total_records,
round(avg(latency_ms), 2) AS avg_latency
FROM read_parquet([
'data/logs_split_v2/logs_auth.parquet',
'data/logs_split_v2/logs_checkout.parquet'
], filename = true)
GROUP BY filename;
5. Manajemen Sumber Daya, Konkurensi & Praktik Produksi
Menjalankan DuckDB pada mesin kerja atau server pengolahan data membutuhkan pemahaman mendalam terhadap mekanisme pengalokasian memori, disk spilling, dan model konkurensi berkas basis data.
5.1 Alokasi Memori Buffer dan Disk Spilling
Secara default, DuckDB mengalokasikan hingga 80% dari total RAM fisik yang terdeteksi pada host. Pada server yang digunakan bersama atau container yang dibatasi cgroups, alokasi memori buffer harus dibatasi secara eksplisit menggunakan perintah SET max_memory.
Koreksi Kritis: Karakteristik Disk Spilling dan Batasan max_memory terhadap OOM
Mekanisme disk spilling (pemindahan blok intermediat ke temp_directory) di DuckDB hanya didukung oleh operator kueri tertentu yang dirancang untuk eksekusi out-of-core, seperti physical sort (ORDER BY), hash join, dan tahapan hash aggregation (GROUP BY). Operator atau pola pipeline kueri lain yang belum/tidak mendukung spilling—seperti window function kompleks tertentu atau subquery non-spillable—tetap mengonsumsi memori secara in-memory dan dapat langsung memicu kegagalan eksekusi (Out of Memory Error).
Selain itu, disk spilling itu sendiri bisa gagal di tengah eksekusi kueri apabila kapasitas penyimpanan pada direktori temporer penuh, kuota I/O terlampaui, atau terjadi kendala I/O sistem berkas.
Parameter max_memory hanya mengatur kuota memori yang berada di bawah kendali Buffer Manager DuckDB. Alokasi memori di luar buffer manager—seperti metadata tabel, overhead thread runtime, alokasi memori heap sistem C/C++, ekstensi pihak ketiga, serta lonjakan konsumsi memori dari proses lain di dalam container/cgroups—tidak dibatasi oleh parameter ini dan tetap rentan terminasi mendadak (SIGKILL) oleh Linux OOM Killer bila batas total memori host atau cgroup tercapai.
Langkah Menyiapkan Direktori Temporer & Konfigurasi Batasan:
# Pastikan direktori temporer sudah dibuat dengan izin tulis yang memadai
mkdir -p ./duckdb_temp
-- Mengonfigurasi parameter pengelolaan resource sesi DuckDB
SET max_memory = '4GB';
SET temp_directory = './duckdb_temp';
SET threads = 4;
-- Memeriksa parameter aktif dalam sesi
SELECT
current_setting('max_memory') AS allocated_ram,
current_setting('temp_directory') AS spill_dir,
current_setting('threads') AS active_threads;
5.2 Model Konkurensi Berkas Basis Data Antarproses
Bila Anda menyimpan tabel ke dalam berkas basis data persisten (misalnya analytics.duckdb), perhatikan arsitektur konkurensi berkas DuckDB:
- Konkurensi Dalam Satu Proses & Koneksi Thread (In-Process): DuckDB mendukung konkurensi multi-thread di dalam satu proses aplikasi, namun penting dipahami bahwa satu objek koneksi DuckDB tidak aman (bukan thread-safe) untuk digunakan secara bersamaan oleh beberapa thread. Pola arsitektur yang benar adalah setiap worker thread harus membuka dan mengelola objek koneksinya masing-masing (pola connection per thread melalui
db.connect()) terhadap instans basis data yang sama. Di bawah model multi-koneksi per thread ini, satu koneksi dapat mengeksekusi operasi penulisan (INSERT/UPDATE) sementara koneksi thread lain secara simultan melakukan kueri pembacaan data analitik berkat mekanisme isolasi transaksi MVCC (Multi-Version Concurrency Control). - Penguncian Eksklusif Lintas-Proses (Single-Writer): Ketika sebuah proses sistem membuka berkas
.duckdbdalam mode baca-tulis (read-write), proses tersebut memegang kunci eksklusif pada berkas tersebut. Tidak ada proses lain (baik pembaca maupun penulis) yang dapat membuka berkas tersebut secara bersamaan. Proses kedua akan langsung gagal dengan pesan kesalahan IO:Could not set lock on file ... Conflicting lock is held. - Akses Multi-Reader Bersamaan Lintas-Proses: Banyak proses terpisah hanya dapat membuka berkas database yang sama secara simultan apabila semua proses membuka koneksi secara eksplisit dalam mode hanya-baca (read-only).
Langkah Persiapan: Membuat Basis Data Persisten & Tabel Uji
Sebelum menguji skenario konkurensi antarproses, kita siapkan berkas basis data analytics.duckdb dan muat tabel server_logs dari berkas Parquet yang telah dihasilkan sebelumnya:
# Inisialisasi basis data persisten dan siapkan tabel server_logs
./duckdb ./analytics.duckdb -c "
CREATE TABLE server_logs AS
SELECT * FROM read_parquet('server_access_logs.parquet');
"
Setelah penulisan selesai dan proses di atas menutup koneksi, kita dapat menguji dua skenario konkurensi terpisah:
Skenario A: Konflik Penguncian Eksklusif (Single-Writer Lock)
Jika Proses 1 sedang membuka ./analytics.duckdb dalam mode baca-tulis standar pada terminal pertama, kemudian Proses 2 mencoba membuka berkas yang sama dari terminal kedua (sesi terminal baru):
Catatan Lingkungan Shell: Variabel lingkungan $WORK_DIR dari terminal pertama tidak diwariskan ke sesi terminal kedua. Pengguna wajib berpindah (cd) ke path direktori kerja yang telah dicatat pada terminal pertama. Pertahankan cwd ini secara konsisten antar blok shell, CLI SQL, dan Python agar path relatif biner (./duckdb) serta berkas data tetap valid:
# [Terminal Kedua - Proses 2]
# 1. Pindah ke direktori kerja lab yang dicatat pada terminal pertama (WORK_DIR tidak diwariskan ke shell baru)
cd /tmp/duckdb_lab_XXXXXX # Ganti /tmp/duckdb_lab_XXXXXX dengan path riil yang dicatat dari terminal pertama
# 2. Upaya membuka berkas yang sedang dikunci oleh proses penulis lain akan langsung gagal:
./duckdb ./analytics.duckdb -c "SELECT count(*) FROM server_logs;"
# Pesan kesalahan yang dihasilkan DuckDB:
# IO Error: Could not set lock on file "./analytics.duckdb": Conflicting lock is held
Skenario B: Pembacaan Bersamaan Multi-Reader (Read-Only)
Banyak proses terpisah dapat membaca tabel server_logs secara simultan asalkan seluruh proses menyertakan flag -readonly (pada CLI) atau parameter read_only=True (pada driver bahasa pemrograman):
# Menghubungkan proses terpisah ke database bersama dalam mode hanya-baca (Read-Only)
./duckdb -readonly ./analytics.duckdb -c "SELECT count(*) AS total_rows FROM server_logs;"
Pada lingkungan pemrograman Python, parameter read_only=True disematkan secara eksplisit saat menginisialisasi koneksi.
Prasyarat Lingkungan Python: Untuk menjalankan contoh integrasi Python dan memanfaatkan konversi hasil kueri ke Pandas DataFrame via metode .df(), pastikan sesi terminal tetap berada di direktori kerja aktif (cwd) yang sama dengan biner DuckDB dan basis data analytics.duckdb (pertahankan cwd antar blok shell, CLI SQL, dan Python). Siapkan virtual environment (venv) terisolasi serta pasang paket duckdb dan pandas (metode .df() memerlukan pustaka Pandas yang terpasang agar tidak memicu ModuleNotFoundError):
# 1. Buat dan aktifkan Python virtual environment (venv)
python3 -m venv .venv
source .venv/bin/activate
# 2. Pasang paket duckdb dan pandas (wajib untuk pemanggilan .df())
pip install duckdb pandas
Setelah dependensi terpasang, jalankan skrip Python berikut untuk membaca data secara aman dalam mode read-only:
import duckdb
# Membuka koneksi read-only agar aman diakses bersama proses lain
con = duckdb.connect('./analytics.duckdb', read_only=True)
df = con.execute("SELECT endpoint, avg(latency_ms) AS mean_latency FROM server_logs GROUP BY endpoint").df()
print(df)
con.close()
6. Ringkasan Praktik Terbaik & Tautan Dokumentasi Resmi
Berikut adalah ringkasan panduan praktis untuk memastikan kueri DuckDB berjalan stabil dan optimal di lingkungan pengembangan maupun produksi:
- Prioritaskan format Parquet dibandingkan CSV untuk dataset berukuran besar guna memanfaatkan efisiensi kompresi kolom, projection pushdown, dan filter pushdown.
- Tentukan skema eksplisit pada pembacaan CSV analitik, dan pahami bahwa deteksi data malformed sangat dipengaruhi oleh kolom yang diproyeksikan serta parameter parser (seperti
ignore_errors = trueataunull_padding = true). - Gunakan direktori latihan baru yang bersih atau daftarkan daftar berkas secara eksplisit sebelum mengeksekusi kueri berbasis pola wildcard (globbing) agar tidak membaca berkas sisa pengujian lain.
- Pahami bahwa disk spilling hanya aktif untuk operator tertentu yang mendukung (seperti sorting, hash join, dan hash aggregation tertentu) serta dapat gagal bila kuota/disk penuh; tetapkan
max_memorysecara cermat dan sadari bahwa alokasi memori non-buffer tetap tidak kebal terhadap Linux OOM Killer. - Pahami bahwa satu proses read-write mengunci berkas basis data dari seluruh proses lain; siapkan tabel terlebih dahulu dan gunakan mode read-only serentak untuk skenario akses pembacaan paralel lintas-proses.
Referensi Dokumentasi Resmi DuckDB
Untuk rujukan mendalam dan pembaruan sintaksis, kunjungi dokumentasi resmi DuckDB:
COMMENTS