Setelah memahami RPO dan RTO di chapter sebelumnya, kita mulai dari keluarga backup yang paling banyak dipakai di dunia PostgreSQL: backup logis. Pendekatannya seperti menulis resep, bukan mengambil foto masakan — alat backup membaca struktur tabel, index, dan baris data, lalu menuliskannya kembali sebagai pernyataan SQL yang bisa dijalankan ulang di server mana pun. File hasilnya bisa dibuka dengan editor teks, diperiksa isinya, dipindahkan lintas versi PostgreSQL, bahkan diedit sebelum dipulihkan. Chapter ini membedah konsepnya: apa yang masuk dan apa yang tertinggal, bagaimana proses ekspor dan pemulihannya bekerja, serta tiga format penyimpanan yang tersedia.
Backup Logis Itu Sebenarnya Apa #
Backup logis mengekspor isi database — definisi objek beserta datanya — menjadi satu berkas yang bisa dibaca manusia atau format arsip terstruktur. pg_dump membaca katalog database (daftar tabel, kolom, tipe, constraint, index, view, function, trigger), lalu menuliskan urutan pernyataan CREATE TABLE, ALTER TABLE, COPY, dan sejenisnya yang jika dijalankan ulang akan membangunkan skema dan data yang sama persis di database tujuan.
Konsekuensi dari pendekatan “resep” ini ada dua sisi. Sisi baiknya: hasilnya portabel — dump dari PostgreSQL di Linux bisa dipulihkan di Windows atau macOS, dari versi 17 ke versi yang lebih baru, karena SQL adalah bahasa yang sama di mana-mana. Sisi beratnya: pemulihan harus memutar ulang seluruh SQL itu satu per satu, termasuk membuat ulang tiap index baris demi baris — jauh lebih lambat daripada sekadar menyalin file untuk database berukuran besar.
Ruang Lingkup: Satu Database atau Seluruh Cluster #
Chapter Konsep Backup menutup dengan pengingat bahwa cluster berisi tiga kelompok harta benda. Backup logis menyentuhnya dengan dua alat yang ruang lingkupnya berbeda:
| Alat | Cakupan | Yang ikut | Yang tertinggal |
|---|---|---|---|
pg_dump | satu database | tabel, data, index, sequence, view, function, trigger | role, database lain, tablespace |
pg_dumpall | seluruh cluster | semua isi tiap database + role + tablespace + definisi database | — |
Baris pertama tabel itu sengaja ditebalkan dalam ingatan Anda: pg_dump tidak menyertakan role, tidak menyertakan pernyataan CREATE DATABASE, dan tidak menyentuh database lain di cluster yang sama. Dump satu database yang sempurna sekalipun tidak bisa membangunkan cluster dari nol — kata sandi akun, izin akses, dan daftar database harus datang dari tempat lain. pg_dumpall hadir persisi untuk menutup lubang itu; chapter pg_dumpall
membahasnya khusus.
Praktik: Mengekspor dan Melihat Isinya #
Mari buktikan sifat “teks SQL” itu langsung. File pertama menyiapkan dua tabel uji Toko Maju Jaya dengan data tetap:
-- Dua tabel uji Toko Maju Jaya untuk mengamati backup logis.
CREATE TABLE produk (
id integer PRIMARY KEY,
nama varchar(40),
harga numeric(10,2)
);
INSERT INTO produk (id, nama, harga) VALUES
(1, 'Kopi Gayo 250g', 45000),
(2, 'Teh Melati 100g', 15000),
(3, 'Gula Aren 500g', 32000);
CREATE TABLE penjualan (
id integer PRIMARY KEY,
produk_id integer REFERENCES produk(id),
qty integer
);
INSERT INTO penjualan (id, produk_id, qty) VALUES
(101, 1, 2),
(102, 3, 1),
(103, 2, 5);
SELECT (SELECT count(*) FROM produk) AS jumlah_produk,
(SELECT count(*) FROM penjualan) AS jumlah_penjualan;Output yang Dihasilkan:
CREATE TABLE
INSERT 0 3
CREATE TABLE
INSERT 0 3
jumlah_produk | jumlah_penjualan
---------------+------------------
3 | 3
(1 row)Skrip berikut menjalankan pg_dump tanpa opsi apa pun — bentuk paling dasarnya — lalu menelusuri isi file hasilnya. Seperti biasa di course ini, psql polos dan pg_dump polos mengarah ke database yang ditunjuk variabel lingkungan PGDATABASE:
#!/bin/bash
# Backup logis: pg_dump mengekspor objek dan data sebagai teks SQL.
pg_dump "$PGDATABASE" > backup_logis.sql
# Ukuran hasil ekspor: jumlah baris dan jumlah byte.
wc -l backup_logis.sql
wc -c backup_logis.sql
# Bukti isinya SQL: perintah CREATE TABLE dan COPY untuk tiap tabel.
grep -E '^(CREATE TABLE|COPY )' backup_logis.sql
# Yang TIDAK ikut di dalamnya: role dan database itu sendiri.
echo "jumlah CREATE ROLE : $(grep -c 'CREATE ROLE' backup_logis.sql)"
echo "jumlah CREATE DATABASE: $(grep -c 'CREATE DATABASE' backup_logis.sql)"Output yang Dihasilkan:
103 backup_logis.sql
2218 backup_logis.sql
CREATE TABLE public.penjualan (
CREATE TABLE public.produk (
COPY public.penjualan (id, produk_id, qty) FROM stdin;
COPY public.produk (id, nama, harga) FROM stdin;
jumlah CREATE ROLE : 0
jumlah CREATE DATABASE: 0Empat pengamatan penting dari keluaran itu:
wc -ldanwc -c: file berukuran 103 baris dan 2218 byte — angka yang tetap sama untuk data yang sama, karena isi dump ditulis dengan urutan baku. Dua barisSEThinggaSET row_securitydi bagian pembuka menyiapkan sesi psql agar restore berperilaku seperti saat dump dibuat.grep -E '^(CREATE TABLE|COPY )': terlihatCREATE TABLE public.penjualan (danCREATE TABLE public.produk (— objek ditulis dengan nama skema lengkap — disusulCOPY public.penjualan (id, produk_id, qty) FROM stdin;untuk tiap tabel berisi data. Inilah wujud backup logis: skema dan data sebagai SQL.- Baris
COPY ... FROM stdin: data baris tidak ditulis sebagaiINSERTsatu per satu, melainkan dalam formatCOPYyang jauh lebih ringkas dan cepat diputar ulang. - Dua baris
jumlah CREATE ...: keduanya0. Tidak ada satu punCREATE ROLEatauCREATE DATABASEdi file ini — bukti nyata bahwa dump satu database tidak memindahkan penghuni cluster. (Sejak PostgreSQL 17.6, dump juga menyisipkan baris\restrictberisi token acak di awal dan akhir file sebagai pengaman keamanan psql — dua dump dari data yang sama karenanya tidak pernah identik byte demi byte, meski isinya selalu sama.)
Pemulihan: Memutar Ulang SQL #
Sisi lain dari backup logis terlihat saat memulihkannya: tidak ada sihir — psql menjalankan file dump sebagai skrip SQL biasa di database tujuan yang kosong:
#!/bin/bash
# Pemulihan backup logis: putar ulang seluruh file SQL ke database kosong.
psql -X -d postgres -v ON_ERROR_STOP=1 \
-c "DROP DATABASE IF EXISTS pulih_logis WITH (FORCE)" \
-c "CREATE DATABASE pulih_logis" > /dev/null
# psql menjalankan file dump sebagai skrip SQL biasa;
# kita saring keluarannya agar hanya tag penting yang terlihat.
psql -X -d pulih_logis -v ON_ERROR_STOP=1 -f backup_logis.sql \
| grep -E '^(CREATE TABLE|COPY )'
# Data kembali utuh di database yang baru.
psql -X -d pulih_logis -c "SELECT id, nama, harga FROM produk ORDER BY id"Output yang Dihasilkan:
CREATE TABLE
CREATE TABLE
COPY 3
COPY 3
id | nama | harga
----+-----------------+----------
1 | Kopi Gayo 250g | 45000.00
2 | Teh Melati 100g | 15000.00
3 | Gula Aren 500g | 32000.00
(3 rows)Tabel dibangunkan oleh CREATE TABLE, data diisikan oleh COPY 3, dan verifikasi akhir menunjukkan tiga produk kembali utuh di pulih_logis. Perhatikan juga pola skripnya: DROP DATABASE IF EXISTS ... WITH (FORCE) di awal membuat skrip aman dijalankan berulang, dan -v ON_ERROR_STOP=1 memastikan kegagalan di tengah jalan langsung menghentikan eksekusi alih-alih meninggalkan restore setengah jadi.
Format Penyimpanan: plain, custom, dan directory #
pg_dump menulis hasilnya dalam satu dari tiga format, dipilih lewat opsi -F:
| Format | Opsi | Wujud file | Alat pemulih | Kompresi | Selektif saat restore |
|---|---|---|---|---|---|
| plain | -Fp (bawaan) | satu file teks .sql | psql | tidak | sulit — harus diedit manual |
| custom | -Fc | satu file biner terkompresi | pg_restore | ya, bawaan gzip | ya — --table, --list |
| directory | -Fd | satu folder berisi file per objek | pg_restore | ya, per objek | ya — --table, --list |
Format plain adalah yang kita pakai di dua contoh di atas: transparan dan portabel maksimal. Format custom mengemas skema dan data dalam satu file terkompresi yang lebih kecil, lebih cepat dipulihkan, dan — karena menyimpan daftar isi — memungkinkan pemulihan selektif per tabel lewat pg_restore. Format directory menambah kemampuan lagi: tiap tabel ditulis sebagai file sendiri sehingga dump maupun restore bisa dijalankan paralel dengan --jobs. Chapter pg_dump
dan pg_restore
membedah format kedua dan ketiga ini sampai ke perintahnya.
Portabilitas: Kekuatan Terbesar Backup Logis #
Satu keunggulan backup logis yang tidak dimiliki keluarga fisik: lintas versi dan arsitektur. Dump teks dari PostgreSQL 17 bisa dipulihkan ke PostgreSQL 18 tanpa proses konversi; dump dari server Linux x86-64 bisa dipulihkan ke server ARM. Proses peningkatan versi major yang memakai pg_dump/pg_restore (pg_upgrade --clone di luar itu) bergantung pada jalan ini. Sebaliknya, keluarga backup fisik menuntut versi major yang sama persis — topik yang dibedah tuntas di chapter Physical Backup
.
Backup logis kini punya wajah utuh di tangan Anda: ekspor sebagai SQL, pemulihan sebagai pemutar-ulang SQL, cakupan per database, dan tiga format penyimpanan. Chapter berikutnya membawa alat utamanya ke meja operasi — pg_dump, mulai dari pemakaian paling sederhana sampai penyaringan per tabel dan pemeriksaan exit code untuk skrip cron.
Referensi #
Berkas Sumber
Berkas pendukung halaman ini dapat diunduh dan dijalankan langsung sesuai penjelasan di atas.
backup_logis.sql
Unduh berkas--
-- PostgreSQL database dump
--
\restrict fRFoemq1JHqPlA3DMp6plSgVp4zxr57h4JLRdyMhIrdLz2Wwi1x0chYRbCQWhhW
-- Dumped from database version 17.11 (Debian 17.11-0+deb13u1)
-- Dumped by pg_dump version 17.11 (Debian 17.11-0+deb13u1)
SET statement_timeout = 0;
SET lock_timeout = 0;
SET idle_in_transaction_session_timeout = 0;
SET transaction_timeout = 0;
SET client_encoding = 'UTF8';
SET standard_conforming_strings = on;
SELECT pg_catalog.set_config('search_path', '', false);
SET check_function_bodies = false;
SET xmloption = content;
SET client_min_messages = warning;
SET row_security = off;
SET default_tablespace = '';
SET default_table_access_method = heap;
--
-- Name: penjualan; Type: TABLE; Schema: public; Owner: postgres
--
CREATE TABLE public.penjualan (
id integer NOT NULL,
produk_id integer,
qty integer
);
ALTER TABLE public.penjualan OWNER TO postgres;
--
-- Name: produk; Type: TABLE; Schema: public; Owner: postgres
--
CREATE TABLE public.produk (
id integer NOT NULL,
nama character varying(40),
harga numeric(10,2)
);
ALTER TABLE public.produk OWNER TO postgres;
--
-- Data for Name: penjualan; Type: TABLE DATA; Schema: public; Owner: postgres
--
COPY public.penjualan (id, produk_id, qty) FROM stdin;
101 1 2
102 3 1
103 2 5
\.
--
-- Data for Name: produk; Type: TABLE DATA; Schema: public; Owner: postgres
--
COPY public.produk (id, nama, harga) FROM stdin;
1 Kopi Gayo 250g 45000.00
2 Teh Melati 100g 15000.00
3 Gula Aren 500g 32000.00
\.
--
-- Name: penjualan penjualan_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public.penjualan
ADD CONSTRAINT penjualan_pkey PRIMARY KEY (id);
--
-- Name: produk produk_pkey; Type: CONSTRAINT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public.produk
ADD CONSTRAINT produk_pkey PRIMARY KEY (id);
--
-- Name: penjualan penjualan_produk_id_fkey; Type: FK CONSTRAINT; Schema: public; Owner: postgres
--
ALTER TABLE ONLY public.penjualan
ADD CONSTRAINT penjualan_produk_id_fkey FOREIGN KEY (produk_id) REFERENCES public.produk(id);
--
-- PostgreSQL database dump complete
--
\unrestrict fRFoemq1JHqPlA3DMp6plSgVp4zxr57h4JLRdyMhIrdLz2Wwi1x0chYRbCQWhhW
ekspor-logis.sh
Unduh berkas#!/bin/bash
# Backup logis: pg_dump mengekspor objek dan data sebagai teks SQL.
pg_dump "$PGDATABASE" > backup_logis.sql
# Ukuran hasil ekspor: jumlah baris dan jumlah byte.
wc -l backup_logis.sql
wc -c backup_logis.sql
# Bukti isinya SQL: perintah CREATE TABLE dan COPY untuk tiap tabel.
grep -E '^(CREATE TABLE|COPY )' backup_logis.sql
# Yang TIDAK ikut di dalamnya: role dan database itu sendiri.
echo "jumlah CREATE ROLE : $(grep -c 'CREATE ROLE' backup_logis.sql)"
echo "jumlah CREATE DATABASE: $(grep -c 'CREATE DATABASE' backup_logis.sql)"
pulihkan-logis.sh
Unduh berkas#!/bin/bash
# Pemulihan backup logis: putar ulang seluruh file SQL ke database kosong.
psql -X -d postgres -v ON_ERROR_STOP=1 \
-c "DROP DATABASE IF EXISTS pulih_logis WITH (FORCE)" \
-c "CREATE DATABASE pulih_logis" > /dev/null
# psql menjalankan file dump sebagai skrip SQL biasa;
# kita saring keluarannya agar hanya tag penting yang terlihat.
psql -X -d pulih_logis -v ON_ERROR_STOP=1 -f backup_logis.sql \
| grep -E '^(CREATE TABLE|COPY )'
# Data kembali utuh di database yang baru.
psql -X -d pulih_logis -c "SELECT id, nama, harga FROM produk ORDER BY id"
siapkan-uji.sql
Unduh berkas-- Dua tabel uji Toko Maju Jaya untuk mengamati backup logis.
CREATE TABLE produk (
id integer PRIMARY KEY,
nama varchar(40),
harga numeric(10,2)
);
INSERT INTO produk (id, nama, harga) VALUES
(1, 'Kopi Gayo 250g', 45000),
(2, 'Teh Melati 100g', 15000),
(3, 'Gula Aren 500g', 32000);
CREATE TABLE penjualan (
id integer PRIMARY KEY,
produk_id integer REFERENCES produk(id),
qty integer
);
INSERT INTO penjualan (id, produk_id, qty) VALUES
(101, 1, 2),
(102, 3, 1),
(103, 2, 5);
SELECT (SELECT count(*) FROM produk) AS jumlah_produk,
(SELECT count(*) FROM penjualan) AS jumlah_penjualan;