37 poin oleh GN⁺ 6 hari lalu | 20 komentar | Bagikan ke WhatsApp
  • Pada akhirnya, semua database adalah sekumpulan file terstruktur di atas file system, sehingga aplikasi tahap awal sering kali bisa memperoleh performa yang cukup hanya dengan mengelola file secara langsung
  • Dengan mengimplementasikan server yang sama dalam Go, Bun, dan Rust lalu membandingkan tiga pendekatan—pemindaian file, map in-memory, dan pencarian biner di disk—ternyata akses file sederhana pun dapat mencapai throughput tinggi
  • Pendekatan map in-memory menunjukkan performa terbaik (hingga 169k req/s), sementara SQLite stabil di 25k req/s tetapi memiliki overhead
  • Sebagian besar layanan dapat menangani hingga sekitar 90 juta DAU hanya dengan satu file SQLite, sehingga pada tahap awal produk database terpisah tidak diperlukan
  • Database mulai dibutuhkan ketika dataset melebihi RAM atau saat diperlukan join, pencarian dengan banyak kondisi, penulisan konkuren, dan transaksi

Apakah Benar-Benar Perlu Database

  • Database pada akhirnya hanyalah kumpulan file; SQLite berupa satu file, sedangkan PostgreSQL terdiri dari direktori dan proses
    • Semua database membaca dan menulis ke file system, bekerja dengan cara yang sama seperti pemanggilan open() di kode
    • Jadi intinya bukan “apakah akan menulis file”, melainkan “apakah memakai file milik database, atau mengelolanya sendiri
    • Banyak aplikasi tahap awal tetap bisa mendapatkan performa yang memadai dengan pengelolaan langsung

Konfigurasi eksperimen

  • Server HTTP yang sama diimplementasikan dalam Go, Bun (TypeScript), dan Rust, lalu dua strategi penyimpanan dibandingkan
    • Menggunakan tiga file JSONL: users.jsonl, products.jsonl, orders.jsonl
    • Membuat data melalui POST /users, mengambil data melalui GET /users/:id
    • Hanya jalur baca (GET) yang dijadikan target benchmark
  • Pendekatan 1: membaca file di setiap request

    • Saat request masuk, file dibuka, semua baris dipindai, JSON di-parse, lalu dicek apakah ID cocok
    • Rata-rata harus membaca setengah isi file, sehingga kompleksitasnya O(n)
    • Semakin besar data, semakin tajam penurunan kecepatan pemrosesan request
  • Pendekatan 2: memuat seluruh data ke memori

    • Saat startup, seluruh file dibaca lalu disimpan ke hash map berbasis ID
    • Penulisan diperbarui ke map dan file sekaligus, sedangkan pembacaan cukup satu lookup map dengan O(1)
    • File berperan sebagai penyimpanan persisten, map berperan sebagai indeks
    • Go memakai sync.RWMutex, Rust memakai RwLock untuk mendukung pembacaan paralel
  • Pendekatan 3: pencarian biner di disk

    • Solusi tengah untuk pencarian cepat tanpa harus memuat semua data ke RAM
    • Dibuat file data yang diurutkan berdasarkan ID dan file indeks fixed-width (58 byte/record)
    • Indeks ditelusuri dengan ReadAt dalam O(log n), lalu satu record dibaca dari offset terkait
    • Saat record baru ditambahkan, urutan menjadi rusak sehingga perlu regenerasi indeks atau merge berkala
    • Pola merge ini mirip cara kerja LSM-tree

Lingkungan benchmark

  • Skala dataset: 10k, 100k, 1M record
  • Alat beban: wrk, menjalankan request GET acak selama 10 detik dengan 4 thread dan 50 koneksi konkuren
  • Diuji pada mesin yang sama (Apple M1 Mac mini, macOS 15) dengan Go 1.26, Bun 1.3, Rust 1.94
  • Pada Go, juga dibandingkan pencarian biner (disk) dan SQLite (modernc.org/sqlite)

Hasil utama

  • Performa linear scan menurun tajam: pada 1M record, Go hanya 23 req/s dan Bun 19 req/s
  • Pencarian biner (disk): dari 10k hingga 1M record hanya turun 15%, dari 45k ke 38k req/s
    • Berkat page cache OS, area indeks bagian atas selalu tetap berada di memori
  • SQLite: mempertahankan performa konsisten di 25k req/s dengan latensi rata-rata 2 ms
  • Pencarian biner sekitar 1,7x lebih cepat daripada SQLite, sehingga pada lookup PK sederhana SQLite tetap memiliki overhead
  • Pendekatan map in-memory memberi performa terbaik: 97k~169k req/s, latensi di bawah 0,5 ms
  • Bun lebih cepat daripada Go: Bun 106k req/s, Go 97k req/s
    • Bun berbasis JavaScriptCore + Zig(uWebSockets) sehingga melewati libuv
  • Rust sangat unggul pada linear scan: 3~6x lebih cepat daripada Go, diduga karena efisiensi parsing JSON dan I/O
  • Pilihan terbaik berdasarkan use case

    • Throughput absolut tertinggi: Rust in-memory map (169k req/s)
    • Terbaik saat data tidak dimuat ke RAM: Go binary search (~40k req/s)
    • Jika butuh SQL: SQLite (25k req/s)
    • Implementasi paling sederhana: Go linear scan (~20 baris kode)

Arti dari 25.000 req/s

  • Trafik web umum diasumsikan memiliki rasio peak:rata-rata = 2:1
    • Rata-rata 12.500 req/s → puncak sekitar 25.000 req/s
  • Diasumsikan setiap pengguna aktif melakukan 10 kali lookup per jam, dengan tingkat koneksi serentak 10% pada saat puncak
    • Rumus request puncak: DAU × 0.000278
  • Hasil perhitungan DAU jenuh untuk tiap pendekatan
    • Go linear scan: 2.8M
    • Go binary search: 144M
    • SQLite: 90M
    • Go in-memory map: 349M
    • Bun in-memory map: 381M
    • Rust in-memory map: 608M
  • Sebagian besar produk tidak pernah mencapai angka ini
    • Contoh: 10.000 pelanggan SaaS → 3 req/s, aplikasi 100.000 DAU → 30 req/s
  • Kesimpulannya, sebagian besar produk tahap awal tidak membutuhkan database
    • Bahkan jika diperlukan, satu file SQLite saja dapat menangani hingga 90 juta DAU

Kapan database dibutuhkan

  • Saat dataset tidak lagi muat di RAM

    • Pada puluhan juta record, indeks saja bisa membutuhkan beberapa GB
    • Diperlukan paging data, dan database menanganinya secara otomatis
  • Saat perlu query berdasarkan field selain ID

    • Pencarian multi-kondisi memerlukan pemindaian file atau map tambahan
    • Jika harus memelihara banyak map, pada dasarnya Anda sedang membangun query engine sendiri
  • Saat diperlukan join

    • Harus membaca dan menggabungkan banyak file, sementara SQL jauh lebih efisien
  • Saat ada penulisan konkuren dari banyak proses

    • In-memory map di tiap instance terpisah, sehingga konsistensi hilang
    • Dibutuhkan satu sumber kebenaran eksternal → peran database
  • Saat perlu penulisan atomik antar-entitas

    • Pembuatan pesanan dan pengurangan stok harus dijamin berhasil atau gagal bersama
    • Perlu implementasi transaction log terpisah, sementara DB menyelesaikannya dengan ACID
    • Jika kendala-kendala ini belum ada, maka alat internal, side project, dan produk tahap awal
    • Dapat berjalan cukup baik dalam RAM pada satu server
    • File JSONL juga mudah dimigrasikan ke database di kemudian hari

Lampiran dan kode yang disediakan

  • Menyertakan kode server Go, Bun, dan Rust
  • Menyediakan seed data dan skrip benchmark terpisah (run_bench.sh)
  • File ZIP berisi go-server/, bun-server/, rust-server/, dan seed.ts
  • Skrip akan melakukan seed data pada tiga skala, menjalankan load test dengan wrk, lalu berhenti

Informasi terkait DB Pro

  • DB Pro** adalah klien database untuk Mac, Windows, dan Linux**

    • Menyediakan fungsi query, penelusuran, dan administrasi dalam satu tempat
    • Mendukung platform web kolaboratif dan AI bawaan
    • Pada versi terbaru, mendukung koneksi ke database SQLite milik Val Town
    • Pada v1.3.0, ditambahkan fitur pembuatan database, editor multi-query, dan koneksi PlanetScale Vitess

20 komentar

 
happing94 6 hari lalu

Omong kosong apa ini

dikira orang pakai db karena performa?

 
botplaysdice 5 hari lalu

Menurut saya ini tulisan yang sangat bagus. Terutama materi yang memuat 'angka' seperti itu memang langka. Di zaman ketika sulit menemukan developer yang setidaknya punya 'gambaran kasar' tentang overhead dari kode yang kita buat dan tech stack yang kita pakai, saya membacanya dengan senang hati.

 
foriequal0 4 hari lalu

Saya juga setuju. Saya rasa ini adalah materi yang memberi intuisi penting tentang mechanical sympathy maupun pengaturan tempo pengembangan. Seperti Latency Numbers Every Programmer Should Know.
Dan saya tidak merasa tulisan ini dibaca seolah satu arah tertentu pasti selalu lebih baik. Justru angka-angka yang ditunjukkan semua pendekatan yang disebut dalam tulisan itu terlihat sebagai "performa yang lebih dari cukup untuk sebagian besar bisnis", jadi saya membacanya sebagai ajakan untuk memilih pendekatan yang sesuai dengan situasi masalah.

 
botplaysdice 5 hari lalu

Balasan-balasan permatanya juga bonus.

 
white9s 5 hari lalu

Kalau memang ada alasan untuk melakukannya seperti ini, mungkin layak dipertimbangkan, bukan? Misalnya kalau ada batasan performa yang sangat ketat.
Tapi dalam kebanyakan kasus, apakah benar ada alasan untuk sengaja memilih ini? Bukan berarti DB tidak punya kelebihan juga..

 
m00nlygreat 5 hari lalu

Ini cuma terasa seperti perubahan cara berpikir, tapi kalian semua sensitif sekali ya.

 
tazuya 5 hari lalu

Benar juga. Saat jumlah pengguna masih belum banyak di tahap awal bisnis, ini bisa dianggap sebagai usulan bahwa alih-alih membeli DB atau membuatnya rumit, bisnis bisa berjalan sampai mapan hanya dengan file I/O dasar.

 
smash8106 5 hari lalu

Saya juga setuju. Dalam layanan, DB kadang dinilai lebih penting daripada yang sebenarnya diperlukan, dan terkadang ada investasi desain yang berlebihan seolah-olah jika normalisasi rusak maka akan jadi masalah besar.
Bukan berarti jangan memakai DB, tetapi cukup bagus jika kita menyegarkan cara pandang tentang mengapa kita memakainya dan apa sebenarnya inti mendasar dari sebuah layanan.
Seperti biasa, yang penting adalah keseimbangan.

 
cafedead 5 hari lalu

Begitu memilih SQLite untuk server produksi, sejak saat itu kita harus terus-menerus memikirkan kapan harus pindah. Dulu biaya DB itu sendiri (biaya pembelian server, IDC, biaya lisensi, dll.) mahal sehingga layak dipertimbangkan, namun sekarang katanya bisa dibangun hanya dengan sekali klik, jadi apakah benar masih perlu dipikirkan?

 
roxie 3 jam lalu

Bahkan sekarang pun DB itu mahal.

 
csjune 4 hari lalu

Tentu saja, untuk "proyek tahap awal atau aplikasi berskala kecil", mungkin tidak perlu database. Bukan cuma database, elemen lain juga bisa asal pilih saja. Masalahnya muncul saat skalanya membesar. Ini cuma tulisan yang melihat angka-angka untuk seru-seruan saja.

 
carnoxen 5 hari lalu

https://hackers.pub/@gnh1201/2025/…

Terkadang tidak perlu memasang database terpisah. Meski hanya untuk Windows...

 
roxie 3 jam lalu

Saya ngakak begitu lihat judulnya.

 
kuthia 5 hari lalu

Saya kadang berpikir apakah entitas-entitas utama memang harus menjamin persistensi melalui RDBMS. Toh sudah ada cukup banyak teknologi alternatif untuk menyediakan SSOT.

 
neptune 5 hari lalu

Kalau SQLite rusak, sudah tidak ada jalan keluar..

 
okxrr 5 hari lalu

Apakah ada kasus di mana sqlite bisa rusak? Saya penasaran. Tentu di luar perpindahan atau penghapusan file yang tidak normal.

 
GN⁺ 6 hari lalu
Komentar Hacker News
  • Saya sangat menyukai tulisan ini. Ini menunjukkan dengan sangat baik betapa cepatnya komputer
    Namun saya tidak setuju dengan kesimpulan di bagian akhirnya. Penulis mengatakan ini tidak berlaku untuk banyak aplikasi yang punya batasan “perlu ditulis oleh beberapa proses secara bersamaan”, tetapi dalam praktiknya, bahkan produk tahap awal pun sering punya worker terpisah seperti cron atau message queue yang juga perlu menulis secara bersamaan
    Kita bisa saja memaksa agar hanya server utama yang menulis, tetapi itu meningkatkan kompleksitas arsitektur
    Jadi dari sudut pandang skala murni saya setuju dengan penulis, tetapi dalam gambaran yang lebih luas saya rasa lebih baik memakai database. Terutama SQLite adalah pilihan yang masuk akal
    Kalau butuh skala, data yang sering diakses tinggal di-cache di memori. Kombinasi yang saya pakai adalah SQLite + cache in-memory

    • Saya juga sering mengalami situasi serupa. Bahkan jika satu server sudah cukup, saat redundansi server mulai dibutuhkan, kita jadi memerlukan network storage dan pada akhirnya condong ke DB yang bisa diakses lewat jaringan
      S3 kadang bisa dipakai, tetapi tetap ada banyak batasan untuk menjadikannya pengganti penuh
    • Sekarang kalau memulai proyek baru, saya default memakai SQLite. Performanya sangat cepat, dan kalau nanti skalanya membesar, migrasi ke Postgres juga mudah
      Jauh lebih sederhana dan murah karena tidak perlu mengelola server DB terpisah atau backup-nya
    • Setelah melihat benchmark Rust 1M, saya kembali sadar betapa cepatnya komputer
  • Saya benar-benar menyukai SQLite, tetapi saya sadar itu bukan jawaban untuk semua masalah
    Saat membuat aplikasi kamus di sisi klien, saya mencoba port SQLite wasm, tetapi file DB-nya lebih besar dari perkiraan, kompresinya juga kurang bagus, dan loading-nya lambat
    Akhirnya saya beralih ke pendekatan membuat indeks langsung dari file TSV asli, lalu dikompresi dengan zstd, dan didekompresi setiap kali di wasm. Ini ternyata jauh lebih cepat daripada SQLite
    Ukuran modul juga turun dari 800KB menjadi 52KB, dan menjalankan beberapa instance sekaligus pun tidak memberatkan
    Untuk pencarian string saya memakai stringzilla, dan itu luar biasa cepat
    SQLite memang hebat, tetapi bukan jawaban untuk setiap situasi

  • Benchmark SQLite itu kurang dioptimalkan
    Cukup tambahkan

    db.SetMaxOpenConns(runtime.NumCPU())
    db.SetMaxIdleConns(runtime.NumCPU())
    

    hanya dengan ini saja performa di mesin saya naik dari 27,700 r/s menjadi 89,687 r/s
    Saya juga mencoba prepared statement dan mengubah timestamp menjadi int, tetapi tidak ada perbedaan besar

  • Tulisannya lumayan, tetapi bagian “semua DB mengakses filesystem dengan open()” itu tidak akurat
    Aplikasi seperti SQLite memakai mmap untuk memetakan file langsung ke ruang memori. Cara ini bisa melewati syscall dan mengaksesnya jauh lebih cepat
    Di bagian akhir tulisan dijelaskan proses membaca seluruh file ke memori, dan rasanya akan lebih baik jika memakai mmap

    • Memang benar tulisan itu menyederhanakan IO pada DB
      Namun saya juga tidak yakin mmap selalu lebih baik. Ada orang yang lebih suka menanganinya langsung di logika aplikasi daripada bergantung pada API OS
      Untuk paper terkait, lihat riset mmap dari CMU
    • Backend store yang dipakai mmap pada akhirnya juga tetap berupa file di filesystem
      Ungkapan “berfungsi seperti open()” memang agak disederhanakan, tetapi secara teknis tetap benar
  • Dulu sekali saya membuat aplikasi web penjualan kecil dengan Perl, dan karena tidak bisa memasang apa pun di server ISP, saya memakai hash berbasis file
    Klien itu terus memakainya selama lebih dari 20 tahun sampai meninggal dunia, lalu keluarganya mengambil alih dan menggantinya dengan Wordpress
    Saat terakhir saya cek, jumlah pesanannya sudah ratusan ribu, tetapi performanya masih baik-baik saja
    Berkat kemajuan hardware, struktur yang terasa seperti hack ini bertahan jauh lebih lama dari perkiraan. Kalau sekarang, rasanya SQLite pun sudah lebih dari cukup

    • Penasaran situs itu menjual produk apa
  • Kalau mencoba mengimplementasikan storage sendiri, kita jadi bisa memahami bagaimana DB bekerja
    Kita harus menangani indeks dan struktur data secara efisien, dan pada akhirnya akan sampai pada kesimpulan “kalau ini bukan main-main, seharusnya sejak awal pakai DB”

  • Relational Databases Aren’t Dinosaurs, They’re Sharks
    Dibanding manfaat kecil yang didapat pada aplikasi mungil, membuang waktu untuk menciptakan ulang roda jauh lebih merugikan

    • Analogi hiu vs dinosaurus itu benar-benar tepat
      Pada zaman Kapur, hiu sudah hampir sama bentuknya dengan sekarang, dan tetap bertahan tanpa banyak perubahan sesudahnya
      Sementara dinosaurus, pterosaurus, dan mosasaurus telah punah, hiu, buaya, dan ular besar masih ada hingga kini hampir tanpa perubahan berkat desain yang teroptimasi
      Saya rasa DB relasional juga seperti itu
  • Menyenangkan membaca tulisan seperti ini
    Meski begitu, saya tetap memakai DB dengan SQL dan transaksi dalam 99% kasus
    Namun belakangan ini, untuk proyek pribadi, saya sempat mengelola data dengan filesystem sederhana berbasis file YAML, dan pada skala saya tidak ada masalah performa sama sekali
    Bisa dibaca manusia dan bisa di-diff lebih penting bagi saya daripada performa
    Tetapi dalam kebanyakan kasus, saya tetap akan memilih DB dengan bahasa query dan konsistensi yang terjamin

  • Pada akhirnya, kita selalu membutuhkan fitur DB dan jaminan ACID
    Setiap kali harus memakai flatfile store legacy, saya selalu kesulitan karena harus memaksakan konsistensi, transaksi, dan bahasa query ke atasnya. Ujung-ujungnya tetap menciptakan ulang roda

  • Saat atomisitas dibutuhkan, DB itu wajib
    Mengimplementasikan write atomik di atas filesystem itu sangat rapuh
    Karena alasan seperti ini, banyak DB mengalami masalah kerusakan data saat crash. Dulu RocksDB di Windows pernah seperti itu

    • Kalau butuh perubahan atomik pada file, saya akan langsung memakai SQLite
      Mengimplementasikannya sendiri terasa seperti tindakan gila. Memang ada baiknya belajar cara menulis dengan aman lewat API OS, tetapi sekarang itu sudah jadi keahlian yang sangat niche
      Selain itu, besar kemungkinan penerus kita tidak bisa memeliharanya. Pada akhirnya tetap akan pindah ke DB
    • Kode dalam tulisan itu suatu hari akan menghasilkan file kosong kalau listrik mati
      Minimal harus menulis ke file sementara di filesystem yang sama, lalu fsync dan ganti dengan rename
    • Untuk kasus sederhana, sebenarnya tidak serapuh itu
      Kalau seluruh DB ditulis ke file sementara, lalu di-flush dan diganti dengan move, itu bersifat atomik di Unix
      Hanya saja ini sama sekali tidak scalable. Update kecil pun mengharuskan seluruh file ditulis ulang, dan tetap perlu manajemen lock. Ini cuma menyelesaikan sebagian dari ACID
    • Kalau dilihat begitu, artinya kita sebenarnya sudah sedang menangani A dalam ACID
      Sebagai catatan, DB OLAP DuckDB juga bekerja sangat baik untuk workload out-of-core
    • Per 2025, Linux + ext4 mendukung single dan multi-block atomic write
      Tautan dokumentasi resmi
 
mstorm 6 hari lalu

Kita bisa saja hidup tanpa kulkas, tapi pasti ada ketidaknyamanan.
Kalau bisa pakai kulkas, tidak ada alasan untuk tidak memakainya.

 
foobarman 5 hari lalu

Apakah Anda pendukung Ilbe, Nora?

 
okxrr 5 hari lalu

Komentar ini tampaknya menunjukkan pola pikir sempit para pengembang Korea dan juga level GeekNews.