Sepanjang tujuh chapter terakhir kita menguasai peralatan backup satu per satu: mysqldump dengan segala opsinya, restore dengan jebakan-jebakannya, binary log, sampai point-in-time recovery. Namun seluruh peralatan itu hanya bekerja bila ada yang menjalankannya — dan manusia, sejujurnya, adalah komponen backup yang paling tidak bisa diandalkan. Administrator yang rajin mencadangkan database setiap malam selama sebulan bisa mulai lupa begitu cuti datang, begitu deadline menumpuk, begitu rutinitas membosankan. Backup scheduling memindahkan tanggung jawab itu dari ingatan manusia ke sistem: strategi terjadwal yang berjalan sendiri setiap malam, memutar arsip lamanya sendiri, dan bersuara saat gagal. Chapter ini merakit seluruh pengetahuan section ini menjadi satu strategi utuh — pola jadwal, cron, skrip, rotasi, retensi, sampai pemantauan.
Strategi: Full Dump Mingguan plus Binary Log Harian #
Kombinasi yang paling umum dipakai database kecil sampai menengah di MySQL menggabungkan dua bab yang sudah kita kuasai. Full dump mingguan diambil dengan mysqldump --single-transaction pada malam Minggu — snapshot lengkap yang menjadi fondasi pemulihan. Binary log harian berperan sebagai rantai inkrementalnya: setiap perubahan data sejak dump tercatat sendiri oleh server, tanpa perintah backup tambahan. Saat insiden jatuh di Jumat malam, prosedur pemulihannya berbunyi: restore full dump hari Minggu, lalu putar ulang binlog Senin sampai Jumat — persis alur point-in-time recovery yang kita pelajari.
Diagram di atas adalah siklusnya dari minggu ke minggu. Satu detail penting: binary log menulis secara kontinu, bukan sekali sehari pada jam tertentu — tugas harian kita hanyalah menjaga rantainya sehat. Cron harian menjalankan FLUSH BINARY LOGS agar file binlog dirotasi sekali per hari, sehingga setiap file kira-kira memuat perubahan satu hari dan ukurannya tetap terkendali. Retensinya dijaga variabel binlog_expire_logs_seconds, dan seperti peringatan chapter binary log: angka retensi itu wajib lebih panjang daripada jarak antara dua full backup, kalau tidak rantai replay terputus diam-diam.
Untuk database yang masih kecil — beberapa gigabyte ke bawah — kombinasi yang lebih sederhana justru lebih bijak: full dump harian tanpa bergantung pada binlog. Dump selesai dalam hitungan menit, restore satu langkah, dan RPO terkunci maksimal 24 jam. Biaya kompleksitas PITR baru terbayar ketika dump harian mulai memakan waktu puluhan menit atau jendela restore melebihi RTO yang ditetapkan.
Cron: Penabuh Jadwal #
Penjadwal standar di dunia Linux dan Unix adalah cron — daemon yang membaca tabel perintah bernama crontab dan menjalankan setiap barisnya pada waktunya. Perintah crontab -e membuka tabel milik user kita untuk disunting; setiap baris terdiri dari lima kolom waktu diikuti satu perintah shell:
| Kolom | Nilai contoh | Arti |
|---|---|---|
| Menit | 0 | Menit ke-0 |
| Jam | 2 | Pukul 02:00 |
| Tanggal dalam bulan | * | Setiap tanggal |
| Bulan | * | Setiap bulan |
| Hari dalam minggu | 0 | Minggu (Senin = 1, Sabtu = 6) |
Dua baris crontab berikut menerapkan strategi mingguan-harian kita — keduanya sengaja diletakkan pukul 02:00, jam ketika trafik aplikasi paling sepi. Baris-baris ini ditulis ke tabel cron user backup, yang dibuka dan disunting lewat perintah crontab -e:
-- @notest
0 2 * * 0 /usr/local/bin/backup-full.sh # Minggu 02:00: full dump + gzip
0 2 * * 1-6 /usr/local/bin/backup-harian.sh # Senin-Sabtu 02:00: rotasi binlog harianBaris pertama membaca: menit 0, jam 2, setiap tanggal (*), setiap bulan (*), hari 0 — Minggu pukul 02:00, jalankan skrip full backup. Baris kedua memakai rentang 1-6 pada kolom terakhir, artinya Senin sampai Sabtu; isinya cukup satu statement mysql -e "FLUSH BINARY LOGS;" ditambah pemeriksaan ringan.
Skrip Backup Lengkap #
Cron menjalankan perintah, tetapi perintah itu sebaiknya bukan satu baris mysqldump panjang yang tertanam di crontab. Praktik yang rapi adalah membungkusnya menjadi skrip bernama, lengkap dengan parameter yang mudah disunting, kompresi, verifikasi, rotasi, dan pencatatan ke log. Inilah bentuknya:
-- @notest
#!/usr/bin/env bash
#backup-full.sh - full dump terkompresi + rotasi otomatis
set -euo pipefail
#---- parameter (sunting bagian ini saja) ----
NAMA_DB="toko"
DIR_TUJUAN="/var/backup/mysql"
SIMPAN_HARI=28
BERKAS_LOG="/var/log/backup-mysql.log"
#---- isi skrip (tidak perlu disentuh) ----
stempel="$(date +%F_%H%M)"
berkas_out="$DIR_TUJUAN/${NAMA_DB}_full_${stempel}.sql.gz"
mkdir -p "$DIR_TUJUAN"
mysqldump --single-transaction --routines --triggers --events \
--databases "$NAMA_DB" | gzip > "$berkas_out"
gzip -t "$berkas_out"
find "$DIR_TUJUAN" -name "${NAMA_DB}_full_*.sql.gz" -mtime +"$SIMPAN_HARI" -delete
printf '%s backup %s -> %s\n' "$(date '+%F %T')" "$NAMA_DB" "$berkas_out" >> "$BERKAS_LOG"Mari kita bedah skrip tersebut baris demi baris:
set -euo pipefail— tiga pengaman shell sekaligus: berhenti saat ada perintah gagal (-e), perlakukan variabel kosong sebagai galat (-u), dan anggap pipa gagal bila salah satu tahapnya gagal (pipefail). Tanpapipefail, pipamysqldump | gzipyang gugur di sisi mysqldump tetap dianggap sukses karena gzip menyimpan sisa data setengah jadi.- Blok parameter — empat variabel ini adalah satu-satunya bagian yang disesuaikan per server. Memisahkan parameter dari logika membuat skrip mudah disalin ke server lain.
stempeldanberkas_out— poladate +%F_%H%Mmenghasilkan nama unik seperti2026-03-08_0200yang sekaligus terbaca sebagai waktu pembuatan; nama bertanggal inilah fondasi rotasi di barisfind.mkdir -p— memastikan folder tujuan ada tanpa error bila sudah ada.mysqldump ... | gzip— perintah inti dari chapter mysqldump: snapshot konsisten (--single-transaction), seluruh objek ikut (--routines --triggers --events), database tercipta sendiri saat restore (--databases), lalu kompresi langsung di pipa sehingga versi tak terkompresi tak pernah menyita disk.gzip -t— membongkar seluruh arsip untuk memastikan tidak korup; berkatset -e, skrip berhenti di sini bila arsip bermasalah — sebelum sempat menghapus backup lama.find ... -delete— rotasi otomatis: arsiptoko_full_*.sql.gzberumur lebih dariSIMPAN_HARIdihapus. Retensi menjadi kebijakan yang ditegakkan mesin, bukan niat baik manusia.printf ... >> log— mencatat satu baris hasil: kapan, database apa, ke file mana. Baris inilah yang dibaca pemantauan.
Menguji Inti Skrip di Server Latihan #
Skrip penuh membutuhkan cron dan path sistem yang tidak tersedia di server latihan, tetapi jantungnya — pipa dump-kompresi beserta verifikasinya — bisa kita jalankan apa adanya. Siapkan dulu belajar_backup yang sudah akrab sepanjang section ini:
DROP DATABASE IF EXISTS belajar_backup;
CREATE DATABASE belajar_backup;
USE belajar_backup;Output yang Dihasilkan:
Query OK, 0 rows affected, 1 warning (0.00 sec)
Query OK, 1 row affected (0.00 sec)
Database changedCREATE 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:
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: 0Sekarang tiga baris inti skrip — versi server latihan dengan folder /tmp dan koneksi eksplisit:
-- @test-bash
mysqldump -h 127.0.0.1 -P 3306 -u root --single-transaction --routines --triggers --events --databases belajar_backup | gzip > /tmp/backup-belajar.sql.gz
gzip -t /tmp/backup-belajar.sql.gz && echo "arsip valid"
zcat /tmp/backup-belajar.sql.gz | wc -l
zcat /tmp/backup-belajar.sql.gz | grep -c "CREATE TABLE"Output yang Dihasilkan:
arsip valid
69
1Tiga keluaran itu adalah bentuk ringkas dari apa yang skrip penuh catat ke log. Baris arsip valid membuktikan arsip lolos uji integritas gzip -t — file benar-benar bisa dibongkar utuh. Angka 69 adalah jumlah baris SQL di dalam arsip, bukti bahwa pipa dump-kompresi mengalirkan seluruh isi database, bukan potongan setengah jadi. Angka 1 menghitung statement CREATE TABLE di dalamnya — satu tabel produk, sesuai isi database kita. Untuk database sungguhan, tiga angka pembanding semacam ini layak dicatat setiap malam: jumlah baris dump yang anjlok drastis adalah alarm dini bahwa skrip hanya mencadangkan sebagian database.
Rotasi dan Retensi: Kebijakan Menyimpan #
Rotasi menjawab “berapa lama file backup boleh hidup”, dan jawabannya sebaiknya ditulis sebagai kebijakan eksplisit, bukan kebiasaan yang tak terdokumentasi. Contoh kebijakan untuk database toko dengan full backup mingguan:
| Lapis backup | Dibuat | Disimpan | Alasan |
|---|---|---|---|
| Full mingguan | Minggu 02:00 | 4 arsip (± 1 bulan) | Fondasi pemulihan tercepat untuk insiden biasa |
| Binary log | Kontinu, dirotasi harian | 8–9 hari | Rantai PITR — wajib melebihi jarak antar full (7 hari) dengan margin |
| Full bulanan | Tanggal 1 setiap bulan | 12 arsip | Titik pemulihan jauh ke belakang untuk kerusakan data yang baru disadari berminggu-minggu |
| Arsip tahunan | Awal tahun | Di luar rotasi | Kepatuhan dan audit — bukan bagian siklus backup |
Pola bertingkat ini dikenal sebagai GFS (grandfather-father-son): arsip bulanan hidup paling lama, mingguan menengah, harian paling pendek. Lapis terakhir sengaja disebut arsip, bukan backup — data yang wajib bertahan lama harus keluar dari siklus rotasi, sesuai pembatasan di chapter konsep backup. Retensi adalah kompromi antara biaya penyimpanan dan jendela mundur untuk kerusakan data yang baru disadari — tulis sebagai kebijakan, lalu biarkan baris find di skrip yang menegakkannya.
Aturan 3-2-1 dalam Praktik #
Aturan 3-2-1 yang kita kenal di chapter konsep backup hanya hidup bila jadwal ikut mengeksekusinya. Tiga salinan berarti: data utama di server, arsip dump di DIR_TUJUAN, dan satu salinan lagi di luar. Dua media berarti kedua arsip itu tidak boleh menumpuk pada perangkat yang sama — disk server dan penyimpanan cadangan, misalnya server backup terpisah atau object storage. Satu salinan offsite berarti menempatkan salinan di luar gedung atau di luar penyedia — cloud storage di region berbeda adalah bentuk paling praktis saat ini.
Penerapannya cukup satu baris tambahan di ekor skrip atau satu baris crontab sendiri: rsync -a /var/backup/mysql/ backup@server-cadangan:/backup/toko/ untuk salinan ke server kedua, atau sinkronisasi ke bucket cloud untuk salinan offsite. Dua hal perlu dijaga jujur: menyalin ke folder lain pada disk yang sama tidak dihitung sebagai salinan kedua, dan rotasi yang menghapus arsip lama harus dijalankan hanya pada sumbernya — kalau rotasi ikut berjalan di salinan offsite, kebijakan retensi bertingkat kita lenyap diam-diam.
Memantau Apa Bila Backup Gagal Diam-diam #
Cron punya satu sifat yang berbahaya: ia senyap saat berhasil, dan sering kali senyap juga saat gagal. Output cron dikirim ke email lokal user — mailbox yang hampir tidak pernah dibaca siapa pun — sehingga backup yang mogok tiga minggu bisa bertahan tanpa satu pun alarm. Pemantauan yang sehat memeriksa tiga sinyal secara berkala:
- Baris log terbaru — skrip kita menulis satu baris per keberhasilan; baris terakhir yang tertua dari 24 jam berarti tidak ada backup semalam.
- Umur arsip termuda —
find "$DIR_TUJUAN" -name "*.sql.gz" -mtime -1seharusnya menemukan satu file; hasil kosong berarti jadwal mati. Umur adalah kebenaran akhir: file yang tidak ada tidak bisa berbohong. - Ukuran arsip termuda — arsip yang tiba-tiba mengecil drastis (misalnya dari 500 MB ke 2 MB) biasanya berarti dump hanya menangkap sebagian database, misalnya karena kredensial yang hak aksesnya dicabut atau database yang tanpa sengaja di-rename.
Sinyal-sinyal itu sebaiknya diumpankan ke saluran yang benar-benar menjangkau manusia: pesan ke grup penjaga, email ke penanggung jawab, atau alert di sistem monitoring yang sudah berjalan di tempat Anda. Bentuk notifikasinya tidak penting; yang penting kegagalan berubah dari catatan sunyi menjadi suara yang harus ditanggapi.
Konsekuensi Tanpa Jadwal #
Mari kita tutup dengan skenario yang paling mudah divisualisasikan. Sebuah database aplikasi berjalan enam bulan tanpa backup terjadwal; satu-satunya dump yang ada berasal dari hari pertama peluncuran. Suatu malam disk server mati, dan restore membawa aplikasi kembali hidup — dengan data pelanggan, stok, dan transaksi setengah tahun yang lalu. Restore-nya sukses sempurna; yang usang bukan caranya, melainkan bahannya. Backup usang menghasilkan restore usang, dan jarak usangnya itulah RPO yang harus ditanggung bisnis.
Jadwal dengan pemantauan adalah satu-satunya cara menjaga angka itu tetap kecil tanpa bergantung pada kedisiplinan harian seseorang. Kombinasi lengkapnya kini sudah kita pegang: strategi mingguan-harian, cron sebagai penabuh, skrip dengan verifikasi dan rotasi, retensi sebagai kebijakan tertulis, 3-2-1 sebagai arsitektur penempatan, dan pemantauan sebagai penjaga malam. Satu pertanyaan terakhir menggantung: dari mana kita tahu semua itu benar-benar bekerja? Jawabannya bukan membaca bab ini, melainkan membuktikannya — dan itulah chapter penutup section ini: menguji prosedur recovery dari awal sampai angka checksumnya cocok.