Perangkat keras punya umur, manusia punya kesalahan, dan software punya bug — ketiganya bertemu di satu tempat: database produksi Anda. Disk yang mati mendadak, sebuah DROP TABLE yang dijalankan di jendela terminal yang salah, atau korupsi halaman setelah kehilangan daya bisa menghapus bertahun-tahun data transaksi dalam hitungan detik. Replikasi dan RAID menolong untuk sebagian skenario, tetapi keduanya menyalin kesalahan secepat menyalin data yang benar. Backup yang tersimpan terpisah adalah satu-satunya jaring pengaman terakhir, dan section ini dimulai dari fondasinya: mengapa backup dibuat, seberapa baik backup itu harus, serta peta jalan memilih strategi yang tepat untuk PostgreSQL.
Tiga Musibah yang Menanti Database Anda #
Kehilangan data jarang datang dari satu arah. Tiga penyebab paling umum di lapangan:
- Kegagalan perangkat keras — disk rusak total, RAID controller mati, atau seluruh server terbakar. Data yang hanya ada di server itu ikut lenyap.
- Kesalahan manusia — pernyataan
UPDATEtanpaWHERE, migrasi yang salah sasaran, atauDROP TABLEyang dijalankan di database produksi alih-alih staging. Ini penyebab nomor satu yang paling sering terjadi. - Korupsi data — halaman file data rusak diam-diam akibat RAM rusak, bug, atau kehilangan daya mendadak; query mulai mengembalikan hasil aneh sampai tabel tidak bisa dibaca.
Ketiganya berujung pada pertanyaan yang sama: dari mana data dikembalikan? Mari kita lihat betapa cepatnya musibah kedua terjadi. File pertama membuat tabel pelanggan Toko Maju Jaya — satu-satunya salinan data yang ada:
-- Satu-satunya salinan data pelanggan Toko Maju Jaya.
-- Tidak ada file backup di mana pun -- hanya tabel ini.
CREATE TABLE pelanggan (
id integer PRIMARY KEY,
nama varchar(40),
email varchar(60)
);
INSERT INTO pelanggan (id, nama, email) VALUES
(1, 'Budi Santoso', '[email protected]'),
(2, 'Siti Aminah', '[email protected]'),
(3, 'Rina Wijaya', '[email protected]');
SELECT id, nama, email FROM pelanggan ORDER BY id;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 3
id | nama | email
----+--------------+------------------
1 | Budi Santoso | [email protected]
2 | Siti Aminah | [email protected]
3 | Rina Wijaya | [email protected]
(3 rows)Tiga baris pelanggan tersimpan rapi. Sekarang datang kesalahan klasiknya — satu DROP TABLE yang seharusnya dijalankan di server uji:
-- Musibah paling klasik: satu pernyataan tanpa WHERE,
-- dijalankan oleh manusia yang salah mengklik jendela terminal.
DROP TABLE pelanggan;
-- Sekejap kemudian seluruh data pelanggan sudah tidak ada,
-- dan tanpa backup tidak ada jalan kembali.
SELECT count(*) AS pelanggan_tersisa FROM pelanggan;Output yang Dihasilkan:
psql:musibah-drop.sql:7: ERROR: relation "pelanggan" does not exist
LINE 1: SELECT count(*) AS pelanggan_tersisa FROM pelanggan;
^Mari kita bedah apa yang barusan terjadi:
- Baris 3 (
DROP TABLE pelanggan): PostgreSQL menjalankanDROPsebagai perintah DDL yang menghapus katalog tabel dan menandai file datanya untuk dihapus. Tidak ada keranjang sampah, tidak ada konfirmasi, dan perintah ini sukses dalam hitungan milidetik. - Baris 7 (
SELECT count(*) ...): Query pencarian baris tersisa langsung gagal denganERROR: relation "pelanggan" does not exist. Tabel beserta tiga baris pelanggan di dalamnya sudah tidak ada — dan tanpa salinan di tempat lain, tidak ada perintah PostgreSQL yang bisa mengembalikannya.
Transaksi, MVCC, dan WAL yang dibahas di section sebelumnya menjaga konsistensi selama server hidup — semuanya tidak berdaya setelah perintah destruktif sudah di-commit. Itulah pekerjaan backup.
RPO: Berapa Banyak Data yang Boleh Hilang #
Sebelum memilih alat, tetapkan dulu dua angka target. Yang pertama adalah RPO (Recovery Point Objective): rentang waktu maksimum data yang boleh hilang saat musibah terjadi. Backup malam hari pukul 24.00 dengan musibah pukul 23.00 berarti RPO nyaris 24 jam — seluruh transaksi hari itu lenyap. Backup mingguan berarti RPO tujuh hari. Sebaliknya, kombinasi base backup dan arsip WAL berkelanjutan mengecilkan RPO ke hitungan menit, dan replikasi streaming menekannya mendekati nol.
RPO adalah keputusan bisnis, bukan keputusan teknis. Aplikasi kasir toko mungkin mentolerir kehilangan lima menit transaksi; ledger perbankan tidak boleh kehilangan satu baris pun. Teknologi hanya menyediakan pilihan — Anda yang menentukan angkanya.
RTO: Seberapa Cepat Layanan Kembali #
Angka kedua adalah RTO (Recovery Time Objective): waktu maksimum yang boleh dihabiskan sebelum layanan kembali melayani. RTO bukan cuma lama menjalankan restore — melainkan seluruh rantainya: mendeteksi musibah, menyiapkan server pengganti, memulihkan data, memverifikasi hasilnya, sampai aplikasi diarahkan kembali. Kedua angka ini paling jelas dibaca berdampingan:
| Strategi cadangan | RPO tipikal | RTO tipikal | Sarana yang lazim |
|---|---|---|---|
| Dump SQL tiap malam | sampai 24 jam | puluhan menit sampai jam | pg_dump + pg_dumpall |
| Base backup + arsip WAL | menit sampai detik | menit sampai jam | pg_basebackup + point-in-time recovery |
| Replikasi streaming + failover | mendekati nol | detik sampai menit | replikasi + alat failover |
Perhatikan bahwa baris terakhir bukan pengganti dua baris di atasnya: replikasi menyalin DROP TABLE ke server cadangan dengan sama cepatnya seperti menyalin data sah. Strategi yang matang memakai lebih dari satu baris tabel ini sekaligus.
Peta Strategi: Backup Logis atau Fisik #
PostgreSQL menyediakan dua keluarga besar strategi backup, dan hampir seluruh section ini berjalan di sepanjang garis pemisahnya:
| Aspek | Backup logis | Backup fisik |
|---|---|---|
| Apa yang disalin | pernyataan SQL yang membangun ulang objek dan data | file data server apa adanya |
| Granularitas | satu database, bahkan satu tabel | seluruh cluster, selalu |
| Ukuran hasil | proporsional data yang dipilih | sebesar seluruh cluster |
| Portabilitas | lintas versi, arsitektur, dan sistem operasi | versi major dan arsitektur harus sama |
| Kecepatan pemulihan | lambat — SQL diputar ulang satu per satu | cepat — file disalin lalu WAL diputar |
| Kemampuan khusus | bisa dibaca dan diedit sebagai teks | fondasi point-in-time recovery |
Backup logis diekspor lewat pg_dump dan pg_dumpall; backup fisik dibangun dengan pg_basebackup. Empat chapter pertama section ini membahas keluarga pertama, tiga chapter terakhir membahas keluarga kedua — dan keduanya pada akhirnya hidup berdampingan di server produksi.
Aturan 3-2-1 #
Berapa pun canggihnya alat yang dipakai, penyimpanan backup mengikuti aturan lama yang masih terbukti: 3 salinan data, tersimpan di 2 jenis media berbeda, dan 1 salinan berada di lokasi terpisah (offsite). Salinan pertama adalah database yang berjalan; dua salinan sisanya adalah backup Anda. Media berbeda berarti backup tidak lagi bergantung pada disk yang sama dengan produksinya — kalau disk itu mati, keduanya mati bersama. Lokasi terpisah melindungi dari kebakaran, banjir, dan ransomware yang mengenkripsi seluruh jaringan kantor sekaligus. Backup yang ditulis ke disk yang sama dengan data utamanya bukanlah salinan — hanya duplikat yang mati bersama majikannya.
Backup yang Tidak Pernah Diuji Bukan Backup #
Backup yang berhasil dibuat belum tentu bisa dipulihkan. File dump bisa korup di tengah penyalinan, skema aplikasi berubah sehingga restore gagal di tengah jalan, atau skrip pemulihan Anda ternyata mengandung kesalahan yang baru terlihat saat insiden. Satu-satunya cara tahu backup bekerja adalah mencoba memulihkannya secara rutin — idealnya otomatis, ke server uji, lengkap dengan verifikasi jumlah baris. Chapter terakhir section ini khusus membahas prosedurnya.
Apa yang Sebenarnya Harus Dicadangkan #
Objek yang harus dilindungi tidak semuanya hidup di dalam satu database. Sebuah cluster PostgreSQL berisi tiga kelompok harta benda: database (tempat tabel dan data hidup), role beserta kata sandinya (akun login milik cluster, dipakai lintas database), dan tablespace (pemetaan lokasi penyimpanan fisik). Dua query berikut menengok dua kelompok pertama langsung dari katalog:
-- Satu cluster PostgreSQL menampung banyak database sekaligus.
-- Tiga database bawaan selalu ada sejak cluster dibuat.
SELECT datname AS database_bawaan
FROM pg_database
WHERE datname IN ('postgres', 'template0', 'template1')
ORDER BY datname;
-- Role juga penghuni cluster, bukan milik satu database.
-- Kolom rolcanlogin menandakan role yang boleh masuk (login).
SELECT rolname, rolcanlogin
FROM pg_roles
WHERE rolname = 'postgres';Output yang Dihasilkan:
database_bawaan
-----------------
postgres
template0
template1
(3 rows)
rolname | rolcanlogin
----------+-------------
postgres | t
(1 row)Tiga database bawaan dan role postgres muncul dari katalog — keduanya penghuni cluster, bukan isi salah satu database. Pembagian ini punya konsekuensi praktis yang mengejutkan pendatang baru: pg_dump, alat backup yang paling sering dipakai, hanya mencadangkan satu database. Role, kata sandi, dan daftar database itu sendiri tidak ikut — memulihkan seluruh cluster hanya dari dump per-database akan meninggalkan server tanpa satu pun akun login. Itulah alasan pg_dumpall ada, dan alasan chapternya hadir di section ini.
Dengan konsep RPO, RTO, peta strategi, dan inventaris cluster di tangan, saatnya beranjak dari teori ke praktik. Langkah pertama kita adalah keluarga yang paling banyak dipakai: backup logis — mengekspor objek dan data menjadi SQL yang bisa dibaca manusia, dibawa ke mana saja, dan dipulihkan lintas versi.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
buat-tabel-pelanggan.sql
Unduh berkas-- Satu-satunya salinan data pelanggan Toko Maju Jaya.
-- Tidak ada file backup di mana pun -- hanya tabel ini.
CREATE TABLE pelanggan (
id integer PRIMARY KEY,
nama varchar(40),
email varchar(60)
);
INSERT INTO pelanggan (id, nama, email) VALUES
(1, 'Budi Santoso', '[email protected]'),
(2, 'Siti Aminah', '[email protected]'),
(3, 'Rina Wijaya', '[email protected]');
SELECT id, nama, email FROM pelanggan ORDER BY id;
inventaris-cluster.sql
Unduh berkas-- Satu cluster PostgreSQL menampung banyak database sekaligus.
-- Tiga database bawaan selalu ada sejak cluster dibuat.
SELECT datname AS database_bawaan
FROM pg_database
WHERE datname IN ('postgres', 'template0', 'template1')
ORDER BY datname;
-- Role juga penghuni cluster, bukan milik satu database.
-- Kolom rolcanlogin menandakan role yang boleh masuk (login).
SELECT rolname, rolcanlogin
FROM pg_roles
WHERE rolname = 'postgres';
musibah-drop.sql
Unduh berkas-- Musibah paling klasik: satu pernyataan tanpa WHERE,
-- dijalankan oleh manusia yang salah mengklik jendela terminal.
DROP TABLE pelanggan;
-- Sekejap kemudian seluruh data pelanggan sudah tidak ada,
-- dan tanpa backup tidak ada jalan kembali.
SELECT count(*) AS pelanggan_tersisa FROM pelanggan;