Bali, Indonesia
Insights

Rutin Cek Binary Log MySQL yang Menumpuk Sebelum Disk Server Penuh

November 2, 2026
0:00
Disk server tiba-tiba penuh, situs mulai error koneksi database, padahal tidak ada upload besar dan file log web server masih wajar. Saat dicek pakai du, yang paling gemuk ternyata folder data MySQL, dan isinya bukan tabel, melainkan deretan file mysql-bin.000xxx yang jumlahnya ratusan. Itu binary log, dan di banyak server ia dibiarkan tumbuh tanpa batas karena tidak ada yang pernah mengatur retensinya. Artikel ini fokus ke langkah yang bisa langsung dijalankan: cek seberapa besar binlog sekarang, cek kenapa tidak terhapus otomatis, bersihkan dengan aman, lalu verifikasi bahwa pembersihan otomatis benar-benar jalan.
Binary log (binlog) adalah catatan semua perubahan data di MySQL atau MariaDB: setiap INSERT, UPDATE, DELETE, dan perubahan struktur tabel. Fungsinya ada dua. Pertama untuk replikasi, karena server replica membaca binlog dari server utama (source) lalu memutar ulang perubahannya. Kedua untuk point-in-time recovery, yaitu mengembalikan database ke kondisi menit tertentu setelah restore backup penuh. Masalahnya, binlog hanya bertambah. MySQL menulis ke satu file sampai ukurannya mencapai max_binlog_size (default biasanya 1 GB), lalu membuka file baru. File lama tidak dihapus kecuali ada aturan retensi. Di MySQL 5.7 dan MariaDB, default expire_logs_days biasanya 0, artinya tidak pernah dihapus otomatis. MySQL 8.0 ke atas sudah punya default retensi 30 hari lewat binlog_expire_logs_seconds, tapi 30 hari di server dengan situs WooCommerce yang ramai, atau plugin yang menulis session dan statistik ke database terus-menerus, bisa berarti puluhan GB. Yang sering membingungkan pemilik VPS: binlog kadang aktif tanpa mereka sadari, misalnya karena template OS, control panel, atau konfigurasi bawaan image. Jadi jangan berasumsi. Cek dulu.
Langkah pertama selalu read-only. Jalankan sebagai root atau user MySQL yang punya hak cukup:
BASH
# Sisa ruang disk per partisi
df -h

# Binlog aktif? Di mana lokasinya? Berapa ukuran maksimal per file?
mysql -e 'SELECT @@log_bin, @@log_bin_basename, @@max_binlog_size;'

# Setting retensi (salah satu bisa error kalau variabelnya tidak ada di versi kamu)
mysql -e 'SELECT @@binlog_expire_logs_seconds;'
mysql -e 'SELECT @@expire_logs_days;'

# Daftar file binlog yang dikenali server, dengan ukuran dalam byte
mysql -e 'SHOW BINARY LOGS;'

# Total ukuran di disk (sesuaikan path dengan hasil log_bin_basename)
du -ch /var/lib/mysql/mysql-bin.[0-9]* | tail -n 1
Cara membaca hasilnya. Kalau @@log_bin bernilai 0, binlog mati dan masalah disk kamu ada di tempat lain. Kalau 1, lihat @@log_bin_basename untuk tahu prefix dan lokasi file. Nama file bisa mysql-bin, binlog, atau nama host server, tergantung konfigurasi, jadi path di perintah du harus disesuaikan. SHOW BINARY LOGS menampilkan kolom Log_name dan File_size, plus kolom Encrypted di MySQL 8. Kalau daftarnya panjang sekali dan file paling awal bertanggal jauh ke belakang, itu tanda retensi tidak jalan. Untuk variabel retensi: binlog_expire_logs_seconds bernilai 0 atau expire_logs_days bernilai 0 berarti tidak ada penghapusan otomatis. Di MySQL 8.4, expire_logs_days sudah tidak ada, jadi query kedua akan error, dan itu normal.
Ini bagian yang sering bikin bingung. Retensi binlog tidak dijalankan seperti cron yang mengecek tiap menit. Server baru mengevaluasi dan menghapus file kedaluwarsa pada momen tertentu, biasanya saat startup dan saat binlog dirotasi ke file baru (karena max_binlog_size tercapai atau ada FLUSH BINARY LOGS). Akibatnya, kalau kamu baru mengubah retensi lewat SET GLOBAL, file lama tidak langsung hilang. Di server yang trafik tulisnya kecil, rotasi bisa jarang terjadi, jadi file lama bertahan lebih lama dari yang kamu kira. Penyebab lain yang perlu dicurigai:
  • Setting hanya ditulis di file konfigurasi yang tidak dibaca. Cek dengan mysqld --verbose --help | grep -A 1 'Default options' untuk melihat urutan file my.cnf yang dibaca, lalu pastikan nilainya muncul di SELECT @@... setelah restart.
  • Dua variabel di-set bersamaan dengan nilai berbeda. Cara MySQL dan MariaDB memprioritaskannya berbeda per versi, dan biasanya ada peringatan di error log. Lebih aman pakai satu saja.
  • File dihapus manual pakai rm. Ini membuat index binlog (file .index) tidak sinkron dengan isi disk, dan file yang masih dibuka oleh mysqld tetap memakan ruang sampai proses ditutup. Polanya mirip yang dibahas di Kenapa Disk Tetap Penuh Padahal File Log Sudah Dihapus.
Jadi aturannya sederhana: jangan hapus file binlog dengan rm. Pakai PURGE BINARY LOGS, karena perintah itu menghapus file sekaligus memperbarui index.
Sebelum membersihkan apa pun, pastikan tidak ada yang masih butuh binlog lama. Ada dua pihak yang mungkin bergantung padanya: replica yang belum selesai membaca, dan strategi backup yang mengandalkan binlog untuk point-in-time recovery. Peringatan: PURGE BINARY LOGS menghapus file secara permanen dan tidak bisa dibatalkan. Kalau sebuah replica masih membutuhkan file yang sudah di-purge, replikasinya akan berhenti dengan error, dan biasanya replica itu harus dibangun ulang dari backup. Kalau tim kamu mengandalkan binlog untuk restore ke titik waktu tertentu, file yang di-purge juga tidak bisa lagi dipakai untuk itu.
BASH
# Di server source: ada replica yang sedang menarik binlog?
mysql -e 'SHOW PROCESSLIST;' | grep -i 'binlog dump'

# Di SETIAP replica: file binlog source yang sedang dibaca dan dieksekusi
mysql -e 'SHOW REPLICA STATUS\G' | grep -E 'Source_Log_File|Relay_Source_Log_File'
# Versi lama atau MariaDB: SHOW SLAVE STATUS\G, kolom Master_Log_File dan Relay_Master_Log_File

# Hapus semua binlog SEBELUM file yang disebut (file itu sendiri tetap ada)
mysql -e "PURGE BINARY LOGS TO 'mysql-bin.000120';"

# Atau berdasarkan waktu
mysql -e 'PURGE BINARY LOGS BEFORE NOW() - INTERVAL 3 DAY;'
Urutan amannya seperti ini:
  1. Kalau baris Binlog Dump tidak muncul di processlist dan kamu tahu server ini memang tidak punya replica, langkah replikasi bisa dilewati.
  2. Kalau ada replica, catat file paling lama yang masih dipakai di antara semua replica. Ambil nilai Relay_Source_Log_File (atau Relay_Master_Log_File) yang paling kecil.
  3. Cek jadwal backup penuh terakhir. Binlog yang lebih tua dari backup penuh terakhir yang valid umumnya sudah tidak dibutuhkan untuk recovery.
  4. Purge pakai TO dengan nama file yang masih dibutuhkan replica paling lambat, atau pakai BEFORE dengan rentang waktu yang lebih pendek dari jarak antar backup.
Saat disk sudah benar-benar penuh, godaan untuk langsung purge semuanya besar sekali. Tahan sebentar dan jalankan dua cek di atas dulu. Cuma butuh semenit, dan bisa mencegah masalah yang jauh lebih lama diperbaiki.
Setelah ruang disk lega, pasang retensi supaya kejadiannya tidak berulang. Tulis di bagian [mysqld] pada file konfigurasi yang benar-benar dibaca server:
INI
[mysqld]
# MySQL 8.0+ dan MariaDB 10.6+: retensi dalam detik (contoh: 7 hari)
binlog_expire_logs_seconds = 604800

# MySQL 5.7 atau MariaDB lama: pakai satuan hari. Pilih salah satu, jangan isi keduanya.
# expire_logs_days = 7
Angka 7 hari di atas hanya contoh. Nilai yang pas tergantung dua hal: seberapa jauh replica kamu mungkin tertinggal (misalnya replica mati saat akhir pekan), dan jarak antar backup penuh. Rule of thumb yang aman: retensi minimal sedikit lebih panjang dari interval backup penuh. Supaya berlaku tanpa restart, set juga secara dinamis dengan SET GLOBAL binlog_expire_logs_seconds = 604800;. Di MySQL 8 ada SET PERSIST, yang menulis nilai ke mysqld-auto.cnf sehingga bertahan setelah restart. MariaDB tidak punya SET PERSIST, jadi file konfigurasi tetap wajib diubah. Lalu picu evaluasi retensi dengan FLUSH BINARY LOGS;. Perintah ini menutup file binlog aktif dan membuka file baru, dan pada saat itulah file yang sudah melewati batas retensi dihapus. Verifikasinya pakai perintah yang sama dengan langkah pertama. Jalankan lagi SELECT @@binlog_expire_logs_seconds; untuk memastikan nilainya terbaca, SHOW BINARY LOGS; untuk melihat file paling awal sekarang tidak lebih tua dari batas retensi, dan df -h untuk memastikan ruang disk memang bertambah. Kalau df belum berubah padahal daftar binlog sudah pendek, cek apakah ada file yang dihapus manual tapi masih dibuka proses, misalnya dengan lsof +L1.
Ketika partisi yang menampung datadir penuh, MySQL tidak bisa lagi menulis data maupun binlog. Perilakunya tergantung versi dan setting. InnoDB biasanya menunggu sampai ada ruang kosong sambil mencatat peringatan di error log, sehingga query menggantung dan situs timeout. Di MySQL modern, kalau binlog gagal ditulis, default binlog_error_action adalah ABORT_SERVER, artinya mysqld berhenti. Dari sisi pengunjung, gejalanya sama: situs menampilkan error koneksi database. Di meja support, keluhan seperti ini sering datang sebagai "database tiba-tiba mati" atau "WordPress error establishing a database connection". Yang pertama kali perlu dicek adalah df -h dan isi datadir, karena binlog yang menumpuk termasuk penyebab yang mudah terlewat. Orang biasanya mencari file log web server atau backup lama, bukan file di dalam folder MySQL. Cek rutinnya ringan. Masukkan SHOW BINARY LOGS dan df -h ke checklist mingguan, atau pasang alert penggunaan disk di monitoring yang sudah kamu pakai. Begitu retensi terpasang dan terverifikasi lewat FLUSH BINARY LOGS, binlog akan berputar sendiri. Tugas kamu tinggal memastikan angka retensinya tetap masuk akal setiap kali ada replica baru atau jadwal backup berubah.