Backup yang tersimpan rapi di disk belum berarti apa-apa sampai ia berhasil menghidupkan kembali sebuah database yang mati. Restore adalah ujian kebenaran dari seluruh keputusan backup kita — dan seperti semua ujian, lebih baik dihadapi pertama kali di ruang latihan daripada di ruang gawat darurat. Chapter ini sengaja mensimulasikan musibah dari awal sampai akhir: kita mencadangkan belajar_backup, mencatat sidik jari datanya, menghapus database tersebut dengan DROP DATABASE, lalu membangkitkannya kembali dari file dump sambil memverifikasi bahwa data yang kembali benar-benar identik dengan yang hilang.

Menyiapkan Cadangan dan Sidik Jari Data #

Kita bangun dulu “korban” latihan kita — database dengan enam produk yang sudah dikenal sepanjang section ini:

sql
DROP DATABASE IF EXISTS belajar_backup;
CREATE DATABASE belajar_backup;
USE belajar_backup;

Output yang Dihasilkan:

text
Query OK, 0 rows affected, 1 warning (0.00 sec)

Query OK, 1 row affected (0.00 sec)

Database changed
sql
CREATE TABLE produk (
  id    INT AUTO_INCREMENT PRIMARY KEY,
  nama  VARCHAR(100) NOT NULL,
  harga INT NOT NULL,
  stok  INT NOT NULL DEFAULT 0
);

INSERT INTO produk (id, nama, harga, stok) VALUES
  (1, 'Kopi Toraja', 52000, 15),
  (2, 'Teh Melati', 28000, 40),
  (3, 'Gula Aren', 32000, 8);

INSERT INTO produk (nama, harga, stok) VALUES
  ('Susu Kental', 38000, 12),
  ('Kopi Robusta', 48000, 20),
  ('Teh Hijau', 30000, 25);

Output yang Dihasilkan:

text
Query OK, 0 rows affected (0.00 sec)

Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

Query OK, 3 rows affected (0.00 sec)
Records: 3  Duplicates: 0  Warnings: 0

Sebelum database jatuh, kita catat dua angka pembanding untuk pasca-restore: jumlah baris, dan checksum — angka sidik jari yang dihitung MySQL dari isi setiap baris table. Dua angka inilah yang nanti kita bandingkan dengan hasil restore:

sql
SELECT COUNT(*) AS jumlah_produk FROM produk;

CHECKSUM TABLE produk;

Output yang Dihasilkan:

text
+---------------+
| jumlah_produk |
+---------------+
|             6 |
+---------------+
1 row in set (0.00 sec)

+-----------------------+----------+
| Table                 | Checksum |
+-----------------------+----------+
| belajar_backup.produk |  1017492 |
+-----------------------+----------+
1 row in set (0.00 sec)

File cadangan dibuat dengan mysqldump persis seperti chapter sebelumnya — dump tanpa --databases, murni isi database:

bash
-- @test-bash
mysqldump -h 127.0.0.1 -P 3306 -u root --single-transaction belajar_backup > /tmp/restore-belajar.sql

Lagi-lagi perintah ini berjalan senyap karena seluruh output mengalir ke file. Kita kini memegang dua artefak: file /tmp/restore-belajar.sql berisi SQL pembangun ulang database, plus catatan jumlah_produk = 6 dan checksum produk sebagai standar emas pembanding.

Simulasi Musibah #

Tibalah momen yang tidak pernah diinginkan administrator: perintah yang salah terketik di server yang salah. Kita jatuhkan belajar_backup dengan sengaja — di dunia nyata, perintah ini mungkin terselip dari skrip yang keliru target, dari kesalahan manusia, atau dari serangan:

bash
-- @test-bash
mysql -h 127.0.0.1 -P 3306 -u root -e "DROP DATABASE belajar_backup;"
mysql -h 127.0.0.1 -P 3306 -u root -N -e "SELECT COUNT(*) FROM information_schema.schemata WHERE schema_name = 'belajar_backup';"

Output yang Dihasilkan:

text
0

Perintah kedua memeriksa information_schema.schemata — katalog semua database yang dikenal server — dan mengembalikan 0: belajar_backup sudah tidak terdaftar. Table, data, procedure, semuanya lenyap dalam satu statement. Namun file /tmp/restore-belajar.sql tetap setia menunggu di disk; mari kita pergunakan.

Jebakan Pertama: Restore ke Database yang Belum Ada #

Insting pertama kebanyakan orang adalah mengarahkan dump langsung ke mysql sambil menyebut nama database tujuan. Kita coba dulu bentuk yang keliru:

bash
-- @test-bash
mysql -h 127.0.0.1 -P 3306 -u root belajar_backup < /tmp/restore-belajar.sql

Output yang Dihasilkan:

text
ERROR 1049 (42000): Unknown database 'belajar_backup'

Error 1049 Unknown database muncul karena dump tanpa --databases tidak memuat CREATE DATABASE — file SQL di dalamnya langsung bicara tentang table, bukan tentang database. Konsekuensinya jelas: database tujuan harus sudah ada sebelum dump dialirkan. (Kalau sebaliknya kita mengalami error 1049 justru saat membuat koneksi biasa, itu artinya nama database salah ketik — dua konteks berbeda, satu pesan galat.)

Restore dari Dump Tanpa –databases #

Alur yang benar memiliki tiga langkah: ciptakan database kosong sebagai wadah, alirkan dump ke dalamnya, lalu verifikasi. Ketiganya kita jalankan sekaligus:

bash
-- @test-bash
mysql -h 127.0.0.1 -P 3306 -u root -e "CREATE DATABASE belajar_backup;"
mysql -h 127.0.0.1 -P 3306 -u root belajar_backup < /tmp/restore-belajar.sql
mysql -h 127.0.0.1 -P 3306 -u root -t -e "SELECT COUNT(*) AS jumlah_produk FROM belajar_backup.produk; CHECKSUM TABLE belajar_backup.produk;"

Output yang Dihasilkan:

text
+---------------+
| jumlah_produk |
+---------------+
|             6 |
+---------------+
+-----------------------+----------+
| Table                 | Checksum |
+-----------------------+----------+
| belajar_backup.produk |  1017492 |
+-----------------------+----------+

Restore yang baik tidak berhenti pada “tidak ada error” — ia dibuktikan dengan angka. Bandingkan output di atas dengan catatan kita sebelum musibah: jumlah_produk kembali 6, dan checksum table produk kembali menunjuk angka yang sama persis. Checksum dihitung dari isi baris demi baris, sehingga angka yang identik adalah bukti kuat bahwa keenam baris kembali dalam keadaan utuh — bukan sekadar jumlahnya sama, tapi isinya sama. Inilah pola verifikasi pasca-restore yang layak dijadikan kebiasaan: COUNT(*) untuk jumlah, CHECKSUM TABLE untuk isi, plus spot-check beberapa baris penting secara manual.

Restore dari Dump –databases #

Sekarang kita rasakan bedanya dump yang dibuat dengan --databases. Karena file-nya memuat CREATE DATABASE dan USE, alur tiga langkah tadi melipat menjadi satu — dan perintah restore tidak lagi menyebut nama database sama sekali:

bash
-- @test-bash
mysqldump -h 127.0.0.1 -P 3306 -u root --single-transaction --databases belajar_backup > /tmp/restore-dengan-db-belajar.sql
mysql -h 127.0.0.1 -P 3306 -u root -e "DROP DATABASE belajar_backup;"
mysql -h 127.0.0.1 -P 3306 -u root < /tmp/restore-dengan-db-belajar.sql
mysql -h 127.0.0.1 -P 3306 -u root -N -e "SELECT COUNT(*) FROM belajar_backup.produk;"

Output yang Dihasilkan:

text
6

Enam baris kembali lagi, kali ini tanpa satu pun CREATE DATABASE manual dari kita. Dump --databases juga membawa charset dan collation database dalam CREATE DATABASE-nya, sehingga identitas tersebut ikut pindah. Prinsip pilihannya sederhana: dump tanpa --databases memberi kita kendali penuh atas wadah tujuan; dump dengan --databases memberi kenyamanan satu perintah. Untuk pemulihan ke database dengan nama yang sama, kenyamanan menang; untuk pemulihan ke nama baru, kendali penuh menang.

Restore ke Server Baru atau Nama Baru #

Dua skenario kerja sering memerlukan variasi dari alur di atas. Yang pertama: restore ke server lain — misalnya membangun replika latihan atau migrasi ke mesin baru. Karena dump logical hanyalah teks SQL, ia portabel: cukup salin file ke server tujuan, pastikan server tujuan menjalankan versi MySQL yang sama atau lebih baru, lalu ulangi alur CREATE DATABASE + mysql < dump. Kedua: restore ke nama database berbeda — misalnya memuat salinan belajar_backup sebagai belajar_uji di server yang sama. Cara paling bersih adalah menyunting dua baris di file dump (CREATE DATABASE dan USE) dengan editor teks sebelum dialirkan; cara cepat tapi kotor adalah restore ke nama asli lalu memindahkan datanya lewat CREATE TABLE ... SELECT — pendekatan yang rawan karena tidak menyalin index dan attribute table secara utuh.

Troubleshooting Restore yang Gagal #

Beberapa pola kegagalan muncul berulang di dunia nyata. Masalah charset: dump dari server utf8mb4 yang dimuat ke server dengan charset default berbeda bisa menghasilkan teks berubah bentuk (mojibake); file dump kita mengandung SET NAMES utf8mb4 di awal yang seharusnya melindungi dari ini, tetapi server target yang charsetnya tak mendukung akan tetap bermasalah. Error collation: pesan Unknown collation: 'utf8mb4_0900_ai_ci' khas muncul bila dump MySQL 8 dimuat ke MariaDB atau MySQL 5.7 yang tidak mengenal collation tersebut — solusinya sunting collation di file dump ke padanan yang didukung target. Error DEFINER: view, procedure, dan trigger membawa klausa DEFINER=user@host; bila user tersebut tidak ada di server tujuan, pembuatannya gagal dengan error 1449 atau 1227. Jalan keluarnya: buat user yang sama di server tujuan, atau sunting baris DEFINER dari file dump sebelum restore. Beda versi: fitur baru di dump versi terbaru bisa ditolak server lama — arah restore yang aman selalu dari versi lama ke baru, bukan sebaliknya.

Sebelum menutup chapter, kita bereskan meja latihan:

bash
-- @test-bash
mysql -h 127.0.0.1 -P 3306 -u root -e "DROP DATABASE IF EXISTS belajar_backup;"

Referensi #

Database kita sudah pernah mati dan hidup kembali dalam satu chapter — sebuah pencapaian yang menenangkan, tetapi juga menyingkap keterbatasan: restore ini mengembalikan dunia ke keadaan saat dump dibuat. Semua perubahan setelah momen itu — transaksi pelanggan, update stok, pendaftaran baru — tetap hilang bersama database yang jatuh. Pertanyaannya bukan lagi “bagaimana restore”, melainkan “bagaimana mengejar waktu yang hilang setelah backup terakhir”. Jawabannya adalah point-in-time recovery, dan bahan bakarnya adalah binary log — dua topik yang kita bahas berurutan mulai chapter berikutnya.