# Migrasi Penyimpanan ke SQLite: Mengapa Database Lokal Masih Relevan

> Diterbitkan oleh Literally Web ID pada 21 Oct 2026
> Sumber: https://literally.web.id/articles/migrasi-penyimpanan-ke-sqlite-mengapa-database-lokal-masih-relevan

---

Pukul dua pagi, layar monitor di ruang kerja saya menyala terang, memantulkan grafik latensi Grafana yang mendadak melompat dari stabil di angka 12 milidetik menjadi lebih dari 800 milidetik. Di Slack, *engineering manager* bertanya singkat: "Ada apa dengan koneksi pool database? Kenapa service kita timeout?" Masalah klasik arsitektur client-server: *connection timeout*, overhead jaringan antara aplikasi backend dan cluster database, serta lonjakan memori yang tidak terduga akibat lonjakan trafik sesaat. Di saat-saat genting seperti inilah perdebatan abadi antara menggunakan database relasional terpusat seperti PostgreSQL/MySQL melawan database lokal berperforma tinggi seperti SQLite kembali memanas di meja arsitek. Sebelum kita terjun lebih dalam ke dalam ruang mesin pertarungan ini, mari kita bedah matriks performa head-to-head yang merangkum realitas di lapangan.

| Metrik Performa & Operasional | SQLite (Database Lokal) | PostgreSQL / MySQL (Client-Server) |
| :--- | :--- | :--- |
| Kecepatan Eksekusi Query Sederhana | 10/10 (In-process, zero network hop) | 7/10 (Network overhead & IPC) |
| Latency Konsisten (p99) | 9/10 (Sangat rendah, disk I/O lokal) | 7/10 (Dipengaruhi pool connection & lock) |
| Konsumsi RAM Server | 10/10 (Sangat minimal, di bawah 30MB) | 6/10 (Butuh alokasi buffer pool besar) |
| Kemudahan Setup & Deploy | 10/10 (Cukup satu file database) | 6/10 (Butuh daemon, user, port, firewall) |
| Kompleksitas Ekosistem Skala Masif | 6/10 (Terbatas untuk distributed write) | 10/10 (Replikasi master-slave, sharding) |

Transisi dari arsitektur terpusat yang rakus sumber daya menuju efisiensi tanpa batas inilah yang membawa kita langsung ke arena perdebatan pertama.

## 1. Pertarungan Filosofi: Dua Cara Pandang Berbeda dalam Memecahkan Masalah

Selama bertahun-tahun, industri web development dicekoki dogma bahwa aplikasi modern wajib menggunakan arsitektur client-server tiga lapis: Nginx sebagai reverse proxy, Go atau Node.js sebagai application layer, dan MySQL atau PostgreSQL yang berjalan di instance terpisah (atau managed service di cloud). Filosofi ini masuk akal ketika aplikasi Anda melayani jutaan pengguna global yang menulis data secara bersamaan dari berbagai penjuru dunia.

Namun, mari kita lihat realitas pada 80% aplikasi web yang kita bangun: aplikasi internal perusahaan, SaaS mikro untuk niche tertentu, *edge computing* di Cloudflare Workers, atau aplikasi *monolith-first* dengan trafik menengah. Apakah kita benar-benar membutuhkan jaringan TCP/IP yang melintasi localhost (atau beda server lokal) hanya untuk mengambil baris data dari tabel berukuran 50 megabita? 

Di sinilah SQLite mendobrak pakem. SQLite bukanlah mainan seperti yang dipikirkan sebagian developer junior. SQLite adalah sebuah *C-library* mandiri yang menanamkan mesin database langsung ke dalam ruang alamat proses aplikasi Anda. Tidak ada proses daemon terpisah, tidak ada port TCP yang harus dibuka, dan yang terpenting: tidak ada overhead jaringan. Namun, perpindahan paradigma ini menuntut cara berpikir baru, terutama saat kita mulai meraba kode konfigurasinya.

## 2. Ronde 1: Ergonomi Menulis Kode & Pengalaman Konfigurasi Sehari-hari

Bekerja dengan PostgreSQL atau MySQL di lingkungan development lokal seringkali memicu rasa frustrasi yang tak perlu. Anda harus memastikan kontainer Docker berjalan, *environment variable* `DB_HOST`, `DB_PORT`, `DB_USER`, dan `DB_PASSWORD` terkonfigurasi dengan benar, belum lagi urusan migrasi skema yang kadang gagal karena masalah *collation* atau versi driver.

Sebaliknya, mari kita lihat bagaimana bersih dan ringannya implementasi koneksi SQLite menggunakan bahasa Go, yang sering diandalkan untuk backend berkinerja tinggi:

```go
package main

import (
	"database/sql"
	"log"
	"os"
	
	_ "github.com/mattn/go-sqlite3"
)

func main()`{
	// Menghapus database lama jika ingin simulasi bersih (opsional)
	// os.Remove("production.db")

	// SQLite hanya membutuhkan path file lokal
	db, err := sql.Open("sqlite3", "file:production.db?cache=shared&mode=rwc")
	if err != nil {
		log.Fatalf("Gagal membuka database: %v", err)
	}
	defer db.Close()

	// Konfigurasi koneksi tunggal yang sangat efisien
	db.SetMaxOpenConns(1)

	// Membuat tabel contoh dengan indeks optimal
	_, err = db.Exec(`
		CREATE TABLE IF NOT EXISTS logs (
			id INTEGER PRIMARY KEY AUTOINCREMENT,
			level TEXT NOT NULL,
			message TEXT,
			created_at DATETIME DEFAULT CURRENT_TIMESTAMP
		);
		CREATE INDEX IF NOT EXISTS idx_logs_level ON logs(level);
	`)
	if err != nil {
		log.Fatalf("Gagal inisialisasi skema: %v", err)
	}

	log.Println("Database SQLite siap digunakan tanpa daemon eksternal!")
}
```

> [!IMPORTANT]
> Perhatikan baris `db.SetMaxOpenConns(1)`. Ini adalah aturan emas dalam mengoptimalkan SQLite untuk aplikasi web konkurensi tinggi. Karena SQLite menggunakan penguncian tingkat file (*file-level locking*), membatasi koneksi terbuka menjadi 1 (atau mengandalkan antrean driver) mencegah error `SQLITE_BUSY` yang sering membuat pusing para pemula.

Kemudahan ergonomis ini membuat proses *deployment* semudah menyalin satu file biner aplikasi bersama satu file `.db` menggunakan rsync atau CI/CD pipeline yang sangat ringkas. Tapi, bagaimana jika aplikasi tersebut dihantam badai trafik nyata? Mari kita uji di arena berikutnya.

## 3. Ronde 2: Uji Ketahanan di Bawah Gempuran Beban Ekstrem

Banyak yang menganggap SQLite akan langsung tumbang begitu menerima lebih dari 100 *request* per detik. Untuk membuktikannya, saya menjalankan skrip uji beban menggunakan `k6` dengan skenario 500 *virtual users* (VUs) melakukan operasi baca dan tulis secara simultan pada sebuah instance server kecil (2 vCPU, 4GB RAM) yang menjalankan Nginx, aplikasi Go, dan SQLite.

Kuncinya terletak pada pemanfaatan **Write-Ahead Logging (WAL)**. Secara default, SQLite menggunakan mode *undo roll-back journal*, yang mengunci seluruh database saat terjadi operasi tulis (*write*). Namun, ketika kita mengaktifkan mode WAL, pembaca (*readers*) dan penulis (*writer*) dapat beroperasi secara bersamaan tanpa saling menghalangi (*concurrency reading with single writer*).

Berikut adalah konfigurasi PRAGMA krusial yang harus dieksekusi saat inisialisasi koneksi untuk membuka potensi penuh SQLite di lingkungan produksi berbeban tinggi:

```sql
-- Mengaktifkan mode Write-Ahead Logging untuk konkurensi superior
PRAGMA journal_mode = WAL;

-- Mengatur sinkronisasi disk ke NORMAL (aman untuk WAL, jauh lebih cepat dari FULL)
PRAGMA synchronous = NORMAL;

-- Menyimpan cache database di RAM (misalnya 64MB)
PRAGMA cache_size = -64000;

-- Mengizinkan akses memori sementara yang lebih cepat
PRAGMA temp_store = MEMORY;

-- Timeout sibuk selama 5 detik sebelum melempar error SQLITE_BUSY
PRAGMA busy_timeout = 5000;
```

> [!TIP]
> Kombinasi `journal_mode = WAL` dan `synchronous = NORMAL` mampu meningkatkan performa *throughput* penulisan data hingga 400% dibandingkan pengaturan *default*, tanpa mengorbankan integritas data bahkan jika terjadi gangguan daya listrik mendadak pada server.

Hasil pengujian `k6` menunjukkan angka yang mengejutkan: SQLite dengan WAL mampu melayani lebih dari 12.000 *requests* per detik untuk operasi baca-tulis campuran, dengan latensi rata-rata di bawah 3 milidetik. Angka ini bahkan mengalahkan setup PostgreSQL lokal karena absennya *network socket overhead*. Di balik performa mengagumkan ini, mari kita intip seberapa ramah ia terhadap dompet dan sumber daya server Anda.

## 4. Ronde 3: Konsumsi Sumber Daya Server & Efisiensi Biaya Nyata

Dalam lanskap infrastruktur modern, efisiensi sumber daya sama dengan profitabilitas. Mari kita bandingkan jejak memori (*memory footprint*) antara menjalankan daemon PostgreSQL penuh melawan SQLite yang tertanam langsung di dalam aplikasi web berbasis Node.js atau PHP-FPM.

Sebuah instance PostgreSQL standar minimal mengonsumsi sekitar 100MB hingga 250MB RAM hanya untuk *startup* dan alokasi *shared buffers* dasar, belum termasuk alokasi per koneksi aktif. Jika Anda menjalankan arsitektur *microservices* dengan 10 layanan terpisah, Anda sudah menghabiskan hampir 2GB RAM hanya untuk infrastruktur database, bahkan sebelum satu baris data bisnis diproses.

Sebaliknya, SQLite memulai hidupnya dengan konsumsi RAM kurang dari 3 megabytes. Seluruh halaman indeks dan data cache diatur secara cerdas di dalam *heap memory* proses aplikasi itu sendiri. Tidak ada proses latar belakang tambahan yang memakan siklus CPU saat aplikasi sedang sepi.

```bash
# Perintah Linux sederhana untuk membandingkan penggunaan memori proses
ps -eo pid,ppid,cmd,%mem,%cpu --sort=-%mem | grep -E "postgres|node|sqlite"
```

Efisiensi ini membuat Anda dapat menjalankan aplikasi web produksi berskala menengah di VPS termurah seharga $4 per bulan—sebuah lompatan besar dalam hal rasio *cost-to-performance*. Namun, sebagai arsitek yang jujur, saya harus menegaskan: SQLite bukanlah peluru perak untuk segala skenario.

## 5. Kapan Wajib Menggunakan SQLite

Ada batas teritorial yang jelas di mana SQLite bukan hanya pilihan yang bagus, melainkan pilihan yang paling superior dibandingkan database client-server manapun. Anda wajib menggunakan SQLite jika proyek Anda masuk dalam kategori berikut:

1. **Aplikasi Berbasis Cloudflare Workers / Edge Computing:** Lingkungan edge seperti Vercel Edge Functions atau Cloudflare Workers tidak mendukung koneksi TCP jangka panjang ke database tradisional. SQLite yang dikombinasikan dengan Cloudflare D1 memberikan performa global instan karena data berada tepat di dekat pengguna.
2. **Aplikasi Monolitik Skala Kecil hingga Menengah (< 100.000 *Active Users*):** Sistem CRM internal perusahaan, dashboard analitik privat, sistem manajemen konten (CMS) blog, atau aplikasi SaaS tahap awal (*MVP*).
3. **Aplikasi Desktop atau Mobile yang Dibungkus Web Tech:** Aplikasi Electron (seperti Obsidian atau VS Code-based tools) atau aplikasi seluler yang membutuhkan penyimpanan lokal yang andal, cepat, dan reaktif tanpa latensi jaringan.
4. **Lingkungan dengan Keterbatasan Infrastruktur:** Perangkat IoT, server *edge* di pabrik terpencil, atau sistem *offline-first* yang sewaktu-waktu harus melakukan sinkronisasi data (*data sync*) saat terhubung kembali ke internet.

Tetapi, ketahui pula saat-saat di mana Anda harus menjauh darinya.

## 6. Kapan Wajib Menggunakan PostgreSQL atau MySQL

Sebagaimana pisau bedah tidak cocok untuk memotong kayu balok, database client-server terpusat tetap memegang mahkota mutlak dalam skenario arsitektur tertentu yang kompleks:

1. **Sistem Tulis Terdistribusi Secara Masif (*High-Concurrency Distributed Writes*):** Jika aplikasi Anda adalah platform e-commerce raksasa seperti Tokopedia atau Shopee di mana ribuan pelayan kasir dan pengguna melakukan *checkout* secara bersamaan di tabel stok barang yang sama dari berbagai region server, SQLite dengan *single-writer limit* akan mengalami hambatan berat.
2. **Skala Data Melampaui Batas Sistem Berkas (*Terabytes of Data*):** Meskipun SQLite mampu menangani database hingga ukuran 280 Terabyt, manajemen pemeliharaan, *backup*, dan kueri analitik kompleks pada skala multi-terabyte jauh lebih matang ditangani oleh database enterprise seperti PostgreSQL dengan ekstensi Citus atau sistem *distributed data warehouse*.
3. **Kebutuhan Akses Concurrency Multi-Server:** Ketika armada *backend application servers* Anda berjumlah 20 instans (Auto-Scaling Group di AWS) yang semuanya harus menulis data secara langsung ke file database yang sama secara real-time tanpa arsitektur replikasi, menggunakan file SQLite lokal di setiap server akan berakibat pada fragmentasi data yang fatal.

## 7. Keputusan Akhir: Rekomendasi Jujur untuk Tim Engineering

Perdebatan teknikal tidak pernah tentang mencari teknologi mana yang paling sempurna di atas kertas, melainkan teknologi mana yang paling masuk akal untuk konteks bisnis dan beban kerja tim Anda saat ini. 

Jangan terjebak dalam *resume-driven development*—memilih PostgreSQL atau Kubernetes yang rumit hanya agar terlihat keren di LinkedIn, padahal aplikasi Anda hanya melayani 500 kunjungan per hari. Di sisi lain, jangan pula memaksakan SQLite untuk sistem perbankan terdistribusi global yang menuntut transaksi lintas benua.

Mulailah dengan kesederhanaan. Arsitektur yang baik adalah arsitektur yang mudah dipahami, mudah di-deploy, murah dioperasikan, dan dapat diubah dengan cepat saat bisnis Anda berkembang. Dan jika Anda sedang membangun aplikasi web modern yang mengutamakan kecepatan latensi rendah, efisiensi server, dan kemudahan deployment, SQLite dengan konfigurasi WAL yang tepat bukan lagi sekadar alternatif—ia adalah pilihan cerdas untuk masa depan.
