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 UPDATE tanpa WHERE, migrasi yang salah sasaran, atau DROP TABLE yang 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:

text
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:

text
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:

  1. Baris 3 (DROP TABLE pelanggan): PostgreSQL menjalankan DROP sebagai 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.
  2. Baris 7 (SELECT count(*) ...): Query pencarian baris tersisa langsung gagal dengan ERROR: 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 cadanganRPO tipikalRTO tipikalSarana yang lazim
Dump SQL tiap malamsampai 24 jampuluhan menit sampai jampg_dump + pg_dumpall
Base backup + arsip WALmenit sampai detikmenit sampai jampg_basebackup + point-in-time recovery
Replikasi streaming + failovermendekati noldetik sampai menitreplikasi + 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:

AspekBackup logisBackup fisik
Apa yang disalinpernyataan SQL yang membangun ulang objek dan datafile data server apa adanya
Granularitassatu database, bahkan satu tabelseluruh cluster, selalu
Ukuran hasilproporsional data yang dipilihsebesar seluruh cluster
Portabilitaslintas versi, arsitektur, dan sistem operasiversi major dan arsitektur harus sama
Kecepatan pemulihanlambat — SQL diputar ulang satu per satucepat — file disalin lalu WAL diputar
Kemampuan khususbisa dibaca dan diedit sebagai teksfondasi 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:

text
 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
sql
-- 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
sql
-- 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
sql
-- 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;