Pindah database adalah keputusan rekayasa, bukan pertaruhan gengsi. Sepanjang course ini kita telah mengukur batas SQLite dengan angka nyata; dua chapter sebelumnya menyiapkan pola produksi untuk bekerja di dalam batas itu. Chapter penutup ini mengajukan pertanyaan yang berbeda: bagaimana mengenali saat batas itu benar-benar terlampaui, kapan justru tidak, dan bagaimana proses pindahnya bila memang harus dilakukan.

Sinyal untuk Tetap #

Empat kondisi ini membuat pindah jadi langkah yang tidak perlu. Pertama, arsitektur kita memang satu mesin — embedded, aplikasi desktop, atau satu server aplikasi. Kedua, beban didominasi pembacaan, wilayah yang paling disukai SQLite karena pembaca tidak saling menghalangi. Ketiga, tim kecil tanpa operator database: tanpa server, tidak ada proses yang harus diamankan, dituning, di-upgrade, dan dimonitor tersendiri. Keempat, kesederhanaan bernilai nyata — satu berkas yang bisa disalin dengan scp dan langsung dibuka di mesin mana pun.

Pemetaan lengkap skenario ini ada di Chapter Kapan Menggunakan SQLite. Yang perlu ditambahkan di sini: bertahan adalah keputusan aktif, bukan kemalasan. Bila tidak satu pun sinyal pindah di bawah terpenuhi, bertahan adalah pilihan yang tepat.

Sinyal untuk Pindah #

SinyalKenapa SQLite mulai tersulitYang dicari di database server
Kontensi tulis berkelanjutangalat database is locked makin sering meski WAL, busy_timeout, retry, dan batching sudah diterapkanbanyak penulis sungguhan yang paralel
Skala horizontalbeberapa mesin aplikasi harus menulis ke data yang sama; berkas di network filesystem berbahayaprotokol jaringan sebagai jalan masuk tunggal
Dataset dan jendela operasibackup, VACUUM, dan pemulihan makin lama seiring membesarnya satu berkasdata tersebar lintas volume
Fitur sisi serverizin per-pengguna, stored procedure, replikasi bawaan tidak adasemuanya adalah fitur produk

Baris pertama adalah sinyal paling jujur: ia baru sah bila strategi Chapter Strategi Concurrency sudah diterapkan penuh dan galat tetap muncul pada jam sibuk. Kalau busy_timeout masih 0 atau transaksi masih panjang, yang perlu diperbaiki adalah aplikasinya, bukan databasenya. Batas 281 terabyte dari Chapter Format Berkas Database bukan angka praktis — yang praktis terasa adalah lamanya jendela pemeliharaan.

Rumput Tetangga Lebih Hijau #

Kerangka pikir yang sehat: pindahlah untuk fitur yang benar-benar dibutuhkan — bukan karena mitos. Daftar sinyal di atas adalah alat ujinya.

Jalur Migrasi #

Ekspor dengan .dump #

Titik berangkat paling sederhana adalah .dump, yang menuliskan seluruh database sebagai skrip SQL — persis seperti yang dibahas pada Chapter Backup dan Restore. Kita buat contoh dengan satu tabel ber-AUTOINCREMENT:

bash
rm -f toko.db
sqlite3 toko.db "CREATE TABLE pelanggan (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  nama TEXT NOT NULL,
  email TEXT NOT NULL
);
INSERT INTO pelanggan (nama, email) VALUES
  ('Budi', '[email protected]'),
  ('Siti', '[email protected]'),
  ('Ani', '[email protected]');"

sqlite3 toko.db .dump > toko.sql
cat toko.sql

Output yang Dihasilkan:

text
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE pelanggan (
  id INTEGER PRIMARY KEY AUTOINCREMENT,
  nama TEXT NOT NULL,
  email TEXT NOT NULL
);
INSERT INTO pelanggan VALUES(1,'Budi','[email protected]');
INSERT INTO pelanggan VALUES(2,'Siti','[email protected]');
INSERT INTO pelanggan VALUES(3,'Ani','[email protected]');
INSERT INTO sqlite_sequence VALUES('pelanggan',3);
COMMIT;

Baris-baris itu tidak semuanya langsung jalan di PostgreSQL atau MySQL. Tiga penyesuaian yang pasti dibutuhkan:

  1. PRAGMA foreign_keys=OFF; — perintah PRAGMA tidak dikenal database server; hapus baris ini.
  2. Kata kunci AUTOINCREMENT — ganti menjadi GENERATED BY DEFAULT AS IDENTITY pada PostgreSQL atau AUTO_INCREMENT pada MySQL. Tanpa penyesuaian ini, kolom id di tujuan tidak akan menghasilkan angka otomatis untuk baris baru.
  3. INSERT INTO sqlite_sequence ... — tabel internal pencatat nomor terpakai (dibahas pada Chapter Constraint pada Tabel) tidak ada di database server; hapus barisnya.

Baris BEGIN TRANSACTION, CREATE TABLE, dan seluruh INSERT data umumnya berpindah tanpa perubahan — gaya kutip dan pemisah nilainya sama. Untuk dataset yang lebih besar atau lintas format, Chapter Export dan Import Data menyediakan jalur CSV.

Audit Tipe sebelum Pindah #

Jebakan yang lebih halus dari sintaks adalah type affinity: kolom INTEGER pada SQLite biasa tetap menerima teks yang tidak bisa dikonversi, dan PostgreSQL akan menolaknya saat proses muat. Audit isi dengan typeof() sebelum ekspor:

bash
sqlite3 toko.db "CREATE TABLE stok (id INTEGER PRIMARY KEY, produk TEXT NOT NULL, jumlah INTEGER);
INSERT INTO stok VALUES
  (1, 'Kopi Gayo 200g', 40),
  (2, 'Teh Hijau 100g', 'dua puluh'),
  (3, 'Gula Aren 500g', 12),
  (4, 'Teh Melati 100g', NULL),
  (5, 'Kopi Tubruk 250g', 'banyak');"

sqlite3 toko.db "SELECT id, produk, jumlah, typeof(jumlah) FROM stok ORDER BY id;"
sqlite3 toko.db "SELECT id, produk, jumlah FROM stok WHERE typeof(jumlah) = 'text' ORDER BY id;"
sqlite3 toko.db "SELECT id, CAST(jumlah AS INTEGER) AS hasil_cast FROM stok WHERE id IN (2, 5) ORDER BY id;"

Output yang Dihasilkan:

text
1|Kopi Gayo 200g|40|integer
2|Teh Hijau 100g|dua puluh|text
3|Gula Aren 500g|12|integer
4|Teh Melati 100g||null
text
2|Teh Hijau 100g|dua puluh
5|Kopi Tubruk 250g|banyak
text
2|0
5|0

Tiga keluaran itu membentuk satu kesimpulan. Query pertama memperlihatkan isi yang tidak pernah diperbolehkan server database mana pun: teks 'dua puluh' dan 'banyak' duduk di kolom INTEGER. Query kedua adalah daftar kerja pembersihannya. Query ketiga menegaskan CAST bukan solusi — teks yang bukan angka menjadi 0, nilai yang sah untuk stok, sehingga perbaikannya harus manual baris demi baris. Untuk data baru, tabel STRICT dari Chapter Tipe Data dan Type Affinity mencegah masalah ini sejak awal.

Perilaku PRAGMA yang Tidak Ikut Pindah #

journal_mode, synchronous, dan busy_timeout adalah kenop perilaku berkas milik SQLite — database server punya model konfigurasi yang berbeda dan tidak memilikinya. Yang perlu diuji ulang pasca-migrasi adalah perilaku, bukan angkanya: misalnya foreign_keys yang di SQLite harus dinyalakan per-koneksi (dibahas pada Chapter Foreign Key) sedangkan server umumnya menegakkannya sejak awal.

Daftar Periksa Keputusan #

PertanyaanMenunjuk pindah bilaMenunjuk bertahan bila
Berapa mesin yang perlu menulis ke data yang sama?lebih dari satusatu
Apakah galat database is locked tetap sering setelah seluruh strategi concurrency diterapkan?yatidak
Ke arah mana beban bergerak?tulisan terus bertambahbaca dominan, stabil
Apakah klien lintas jaringan perlu akses langsung?yasemua lewat aplikasi
Apakah butuh izin per-pengguna di level database?yacukup di lapisan aplikasi
Siapa yang mengurus operasionalnya?tim punya operator databasetim tanpa operator

Jawaban yang menunjuk pindah mengharuskan kita jujur menyiapkan tenaga operasionalnya. Sisanya — mayoritas sistem yang kita bangun sehari-hari — justru menikmati keringkasan SQLite: satu berkas, tanpa server, dengan batas yang sudah kita pahami dan kelola sepanjang course ini.