Query Planner
Mesin database di browser yang menampilkan setiap rencana yang dipertimbangkan dan letak taksiran barisnya meleset

- galat taksiran di puncak, terlihat per node
- 300×
- untuk merencanakan ulang dan menjalankan join empat arah
- < 16 ms
- visualisasi berbeda, semuanya dibuat sendiri
- 8
- permintaan jaringan; tidak ada yang keluar dari browser
- 0
Di halaman ini
Masalahnya
Saat sebuah query database lambat, saran yang biasa muncul adalah menambah index. Sering kali masalahnya bukan di situ. Sebelum menjalankan query, database menaksir berapa baris yang akan dihasilkan setiap langkah, berdasarkan ringkasan statistik kecil dari tabelnya. Kalau taksirannya salah, database dengan yakin memilih rencana yang lambat. Tidak ada yang rusak dan tidak ada index yang kurang. Rencananya dipilih dengan benar, hanya saja dari keyakinan yang keliru.
Salah satu penyebab umumnya adalah asumsi independensi. Saat diminta baris dengan kota Balikpapan dan provinsi Kalimantan Timur, database mengalikan dua peluangnya seolah kota tidak memberi petunjuk apa pun soal provinsi. Tebakannya satu baris dari sepuluh ribu. Kenyataannya satu dari seratus.
Alat standarnya, EXPLAIN, hanya menampilkan rencana pemenang. Kandidat yang kalah dan selisih antara taksiran dan kenyataan tidak terlihat. Saya membuat Query Planner untuk engineer backend dan data, administrator database, serta siapa pun yang mengajar atau mempelajari optimasi query.
Yang saya bangun
Saya menulis mesin database utuh yang jalan di browser
Membungkus mesin yang sudah ada jauh lebih mudah. Tapi tidak ada mesin yang menunjukkan rencana yang dibuangnya, atau menaruh taksirannya di samping jumlah sebenarnya di setiap langkah. Hanya mesin yang saya kendalikan sendiri yang bisa melakukan keduanya. Jadi aplikasi ini punya parser query, penyimpanan tabel, index, statistik, model biaya, pencarian rencana, dan eksekutor dengan sembilan operator sendiri. Nama operatornya sama dengan Postgres, jadi yang dipelajari di sini langsung terpakai saat membaca EXPLAIN sungguhan.
Planner tidak boleh melihat datanya
Planner yang bisa mengintip baris aslinya tidak sedang merencanakan. Saya membuat kode planner tidak bisa mengimpor kode penyimpanan atau eksekutor, dan linter menggagalkan build kalau aturan itu dilanggar. Planner hanya membaca statistik. Setiap taksiran juga membawa penjelasannya sendiri: metode yang dipakai, inputnya, dan asumsinya. Layar menggambar penjelasan itu apa adanya, jadi tampilan tidak mungkin berbeda dari hitungannya.
Setiap rencana yang kalah saya simpan
Pencarian rencana klasik membuang kandidat begitu ada yang lebih murah. Saya menyimpan semua kandidat, beserta urutan pengisiannya. Untuk delapan tabel, itu 255 sel dengan memori yang sangat kecil. Inilah yang membuat animasinya bisa memutar ulang pencarian yang sebenarnya. Saya juga menyimpan rencana yang unggul hanya karena urutan hasilnya. Tanpa itu, merge join tidak akan pernah menang, dan aplikasi ini akan mengajarkan hal yang keliru.
Saya mengujinya terhadap Postgres asli
Postgres yang dikompilasi agar jalan di browser menjadi rujukan saat tes. Data yang sama dimasukkan ke kedua mesin, dan tes memastikan planner saya memilih bentuk rencana yang sama. Kalau keduanya berbeda, penyebabnya saya dokumentasikan di dalam aplikasi, tidak saya akali sampai cocok. Tes terpisah memastikan setiap rencana yang mungkin untuk satu query memberi hasil yang sama, dan tes inilah yang menangkap bug di eksekutor.
Satu detail data hampir menyembunyikan efeknya
Dataset Indonesia yang realistis awalnya menyusun kota berkelompok per provinsi. Generatornya memilih kota berdasarkan posisi di daftar, jadi pengelompokan itu menyusutkan galat independensi dari sekitar 30× menjadi 3×. Saya mengurutkan ulang daftarnya berdasarkan jumlah penduduk, yang juga lebih realistis, dan efeknya kembali muncul.
Hasilnya
Query Planner sudah live dan terbuka untuk umum. Geser pengaturan biaya penyimpanan dari 4,0 ke arah 1,1, penyesuaian yang biasa dipakai untuk SSD, dan batang biaya saling menyilang saat rencana pemenang berganti. Join empat tabel direncanakan dan dijalankan ulang dalam waktu kurang dari 16 milidetik, satu frame di 60 frame per detik. Pohon rencana menampilkan taksiran di samping jumlah sebenarnya, dan galatnya membesar: 1,2× di pemindaian, 8× setelah satu join, 300× di puncak.
Grafik korelasi menggambar asumsi yang salah sebagai persegi panjang yang terlepas dari data asli. Satu klik menambah statistik multi-kolom, dan taksirannya langsung menempel ke kenyataan. Tidak ada data yang keluar dari browser, dan setiap rencana bisa dibagikan sebagai tautan. Saya membangunnya dalam satu sesi sekitar 16,5 jam pada 3 September 2026.
Di balik layarDetail teknis untuk engineer
- Mesin: parser SQL recursive-descent untuk subset yang sempit, penyimpanan kolumnar, index B+tree yang dimuat sekaligus, reservoir sampling (dengan seed, default 30.000 baris), histogram equi-depth, daftar MCV, dan ketiga jenis statistik multivariat (functional dependency, n-distinct, MCV multivariat).
- Biaya berupa pasangan
(startup, total), sehinggaLIMIT 10bisa memilih rencana berbeda. Dynamic programming Selinger dengan penyimpanan interesting order;tests/orders.test.tsmencakup kasus yang terbalik kalau penyimpanan itu dimatikan. - Eksekutor model volcano: seq scan, index scan, nested loop, hash join, merge join, sort, hash aggregate, group aggregate, limit. Waktu per node diambil dari penghitung kasar supaya instrumentasi tidak mengacaukan pengukuran.
- ESLint
no-restricted-importsmencegahsrc/planner/mengimporsrc/storage/atausrc/executor/.Math.randomdilarang di engine; semua keacakan memakai seed. - Setiap selektivitas mengembalikan
SelectivityTrace(mcv,histogram,independence,dependency, …) yang dirender oleh tampilan histogram dan korelasi. - PGlite (Postgres di WebAssembly) hanya devDependency.
src/planner/divergences.tsmencatat setiap perbedaan, dan tes oracle memastikan masing-masing tetap berbeda. Commit5ba8322berjudul "the PGlite oracle, and what it caught". - Daftar kota ada di
src/storage/datasets/wilayah.ts; sampler-nya Zipf berdasarkan posisi di daftar. - Delapan instrumen SVG buatan sendiri, termasuk tidy tree Reingold–Tilford, satu loop
requestAnimationFrame, dan sistem token dua tema yang dibangun ulang dalam revisi desain di tengah proyek (a073496). tests/copy.test.tsmelarang em-dash dan kalimat pemasaran di teks yang dirilis. Tema tidak ikut disimpan di URL yang dibagikan.- Tiga dependensi runtime (React, React DOM, typeface Geist). JS 98 KB gzip dari anggaran 350 KB. Nol permintaan jaringan saat runtime.
- CI: typecheck, lint, test, oracle, build, lalu deploy ke GitHub Pages.
- 294 tes (261 unit dan integrasi, 33 oracle) di 13 file. ~13.500 baris. 36 commit.
Tangkapan layar
Sedang merekrut, atau membangun hal serupa?
Saya terbuka untuk posisi full-time dan proyek freelance tertentu.