MySQL Binary Log ile Point-in-Time Recovery Rehberi

Bir production veritabanında yanlışlıkla DROP TABLE çalıştırdığınız anı hayal edin. Kalp atışınız durur, eller titrer ve aklınıza gelen ilk şey “yedeğim ne zaman alındı?” olur. Eğer binary log aktifse ve point-in-time recovery (PITR) nasıl yapılır biliyorsanız, bu felaket senaryosu aslında birkaç dakikalık bir kurtarma operasyonuna dönüşür. Binary log, MySQL’in en güçlü silahlarından biridir ama çoğu sysadmin bunu sadece replikasyon için bilir. Bugün bu konuyu A’dan Z’ye masaya yatırıyoruz.

Binary Log Nedir ve Neden Önemlidir

MySQL binary log (binlog), veritabanında yapılan tüm değişiklikleri sıralı biçimde kaydeden bir günlük dosyasıdır. SELECT sorguları buraya yazılmaz, yalnızca veriyi değiştiren işlemler kaydedilir: INSERT, UPDATE, DELETE, DDL komutları, stored procedure çağrıları…

Binary log’un üç temel kullanım alanı vardır:

  • Replikasyon: Master’dan slave’e değişikliklerin iletilmesi binlog üzerinden çalışır
  • Point-in-Time Recovery: Belirli bir zaman noktasına kadar veriyi geri yükleme
  • Denetim ve Analiz: Kimin ne zaman ne yaptığını izleme

PITR mantığı şöyle işler: Tam yedeği geri yüklersiniz, ardından binary log’lardan o yedeğin alındığı andan itibaren istediğiniz ana kadar olan değişiklikleri tekrar uygularsınız. Bu sayede “dün gece 23:45’teki haline geri dön” gibi cerrahi müdahaleler yapabilirsiniz.

Binary Log Yapılandırması

Binary log varsayılan olarak MySQL 8.0’da açıktır, MariaDB’de ise kapalı gelir. Durumu kontrol edelim:

mysql -u root -p -e "SHOW VARIABLES LIKE 'log_bin';"
mysql -u root -p -e "SHOW MASTER STATUSG"

Eğer log_bin değeri OFF ise, yapılandırma dosyasını düzenlemeniz gerekir. /etc/mysql/mysql.conf.d/mysqld.cnf veya /etc/my.cnf dosyasını açın:

[mysqld]
# Binary log temel ayarları
log_bin                 = /var/log/mysql/mysql-bin.log
binlog_format           = ROW
server_id               = 1
expire_logs_days        = 7        # MySQL 5.7 ve altı
binlog_expire_logs_seconds = 604800  # MySQL 8.0+ (7 gün)

# Performans ve güvenlik için önerilen ayarlar
sync_binlog             = 1
innodb_flush_log_at_trx_commit = 1
max_binlog_size         = 100M
binlog_row_image        = FULL

Ayarları açıklayalım:

  • log_bin: Binary log dosyalarının konumu ve ön eki
  • binlog_format: ROW, STATEMENT veya MIXED olabilir. ROW en güvenli seçenektir çünkü her satır değişikliğini kaydeder
  • server_id: Replikasyon için zorunlu, her sunucu benzersiz bir ID almalı
  • sync_binlog = 1: Her transaction’da diski senkronize eder, sistem çökmesinde veri kaybını önler
  • binlog_row_image = FULL: Değişen satırın önceki ve sonraki halinin tamamını kaydeder

Ayarları uygulamak için MySQL’i yeniden başlatın:

sudo systemctl restart mysql
# veya MariaDB için
sudo systemctl restart mariadb

Binary Log Formatları

Binlog formatını anlamak kritiktir çünkü PITR sürecinizi doğrudan etkiler.

STATEMENT formatında her SQL ifadesi aynen kaydedilir. Hafiftir ama NOW(), RAND() gibi non-deterministic fonksiyonlar içeren sorgularda replikasyon tutarsızlığa yol açar.

ROW formatında her etkilenen satırın değişimi kaydedilir. Disk kullanımı daha fazladır ama güvenilirdir. Production için bu format önerilir.

MIXED format, MySQL’in uygun gördüğü yerde STATEMENT, gerektiğinde ROW kullanmasıdır. Teoride iyi görünse de production’da ROW’u tercih edin.

Mevcut formatı kontrol edin:

mysql -u root -p -e "SHOW VARIABLES LIKE 'binlog_format';"

Binary Log Dosyalarını İnceleme

Binlog dosyaları binary formatta olduğu için doğrudan açamazsınız. mysqlbinlog aracını kullanmanız gerekir.

# Mevcut binary log dosyalarını listele
mysql -u root -p -e "SHOW BINARY LOGS;"

# Belirli bir log dosyasını human-readable formata çevir
mysqlbinlog /var/log/mysql/mysql-bin.000042

# Tarih/saat aralığına göre filtrele
mysqlbinlog --start-datetime="2024-01-15 10:00:00" 
            --stop-datetime="2024-01-15 11:30:00" 
            /var/log/mysql/mysql-bin.000042

# Sadece belirli bir database için
mysqlbinlog --database=production_db 
            /var/log/mysql/mysql-bin.000042

# Birden fazla log dosyasını art arda işle
mysqlbinlog /var/log/mysql/mysql-bin.000040 
            /var/log/mysql/mysql-bin.000041 
            /var/log/mysql/mysql-bin.000042

ROW formatında log’lar doğrudan okunabilir değildir. Bunu çözmek için:

mysqlbinlog --base64-output=DECODE-ROWS 
            --verbose 
            /var/log/mysql/mysql-bin.000042

--verbose flagı ile INSERT, UPDATE, DELETE işlemlerini pseudo-SQL formatında görebilirsiniz. Hangi satırın ne olduğunu anlamak için bu çok işe yarar.

Gerçek Dünya Senaryosu: Yanlışlıkla DROP TABLE

Şimdi gerçek bir felaket senaryosunu adım adım çözelim. Diyelim ki production’da customers tablosunu yanlışlıkla sildik.

Senaryo: 15 Ocak 2024, saat 14:32’de customers tablosu DROP edildi. Son tam yedeğimiz o gün sabah 03:00’te alındı.

Adım 1: Yedekten Geri Yükleme

# MySQL servisini durdurmanıza gerek yok, ama büyük restore'larda tercih edilir
mysql -u root -p production_db < /backup/production_db_2024-01-15_03-00.sql

# Eğer mysqldump kullanıyorsanız ve yedekte binary log pozisyonu varsa
grep "MASTER_LOG" /backup/production_db_2024-01-15_03-00.sql
# Bu size backup alındığındaki binlog pozisyonunu verir
# Örnek çıktı: -- CHANGE MASTER TO MASTER_LOG_FILE='mysql-bin.000041', MASTER_LOG_POS=15234;

Yedeği alırken --master-data=2 seçeneğini kullandıysanız, backup dosyasının içinde binlog pozisyonu yorum satırı olarak bulunur. Bu nokta PITR’ın başlangıç noktasıdır.

Adım 2: Felaketin Tam Zamanını Bulma

# DROP TABLE komutunu binary log'da bul
mysqlbinlog --start-datetime="2024-01-15 03:00:00" 
            --verbose 
            /var/log/mysql/mysql-bin.000041 
            /var/log/mysql/mysql-bin.000042 | grep -i "drop table" -A 5 -B 5

Diyelim ki çıktıda şunu gördünüz:

# at 892145
#240115 14:32:17 server id 1  end_log_pos 892312
DROP TABLE `customers`

Bu bize iki önemli bilgi verir: Olayın log pozisyonu (892145) ve zamanı (14:32:17).

Adım 3: Point-in-Time Recovery Uygulaması

Şimdi yedekten sonraki değişiklikleri, DROP TABLE komutunun hemen öncesine kadar uygulayacağız:

# Yöntem 1: Zaman bazlı (daha kolay ama dikkatli olun)
mysqlbinlog --start-datetime="2024-01-15 03:00:00" 
            --stop-datetime="2024-01-15 14:32:16" 
            --database=production_db 
            /var/log/mysql/mysql-bin.000041 
            /var/log/mysql/mysql-bin.000042 | mysql -u root -p production_db

# Yöntem 2: Pozisyon bazlı (daha güvenilir)
mysqlbinlog --start-position=15234 
            --stop-position=892145 
            --database=production_db 
            /var/log/mysql/mysql-bin.000041 
            /var/log/mysql/mysql-bin.000042 | mysql -u root -p production_db

Pozisyon bazlı yöntem daha güvenilirdir çünkü zaman bazlı yöntemde birden fazla işlem aynı saniyede gerçekleşmiş olabilir.

Adım 4: Doğrulama

mysql -u root -p production_db -e "SELECT COUNT(*) FROM customers;"
mysql -u root -p production_db -e "SELECT MAX(created_at) FROM customers;"

Eğer sonuçlar mantıklı görünüyorsa, DROP Table’dan önceki son kayıt zamanına kadar olan tüm veriyi kurtardınız.

Otomatik Yedek Alma: mysqldump ile Binlog Entegrasyonu

PITR’ın çalışması için yedek alırken binlog pozisyonunu da kaydetmeniz şarttır:

#!/bin/bash
# /usr/local/bin/mysql_backup.sh

BACKUP_DIR="/backup/mysql"
DATE=$(date +%Y-%m-%d_%H-%M)
DB_NAME="production_db"
MYSQL_USER="backup_user"
MYSQL_PASS="güçlü_şifre"

mkdir -p "$BACKUP_DIR"

# --master-data=2 ile binlog pozisyonu yorum olarak eklenir
# --single-transaction InnoDB için tutarlı yedek alır
# --flush-logs yeni bir binlog dosyası başlatır
mysqldump 
    --user="$MYSQL_USER" 
    --password="$MYSQL_PASS" 
    --master-data=2 
    --single-transaction 
    --flush-logs 
    --routines 
    --triggers 
    --events 
    "$DB_NAME" | gzip > "$BACKUP_DIR/${DB_NAME}_${DATE}.sql.gz"

# Yedeğin başarılı olup olmadığını kontrol et
if [ $? -eq 0 ]; then
    echo "Yedek başarıyla alındı: ${DB_NAME}_${DATE}.sql.gz"
    # 7 günden eski yedekleri temizle
    find "$BACKUP_DIR" -name "*.sql.gz" -mtime +7 -delete
else
    echo "HATA: Yedek alınamadı!" >&2
    exit 1
fi

Bu scripti crontab’a ekleyin:

# Her gece 03:00'te çalıştır
0 3 * * * /usr/local/bin/mysql_backup.sh >> /var/log/mysql_backup.log 2>&1

Binary Log Yönetimi

Binary log’lar zamanla disk alanını doldurabilir. Yönetimi için bazı pratik komutlar:

# Mevcut binary log'ları listele
mysql -u root -p -e "SHOW BINARY LOGS;"

# Belirli bir log'a kadar olan eski logları sil
mysql -u root -p -e "PURGE BINARY LOGS TO 'mysql-bin.000040';"

# Belirli bir tarihten öncekileri sil
mysql -u root -p -e "PURGE BINARY LOGS BEFORE '2024-01-08 00:00:00';"

# Tüm binary log'ları sıfırla (DİKKATLİ KULLANIN!)
# Bu komutu replikasyon varken ASLA çalıştırmayın
mysql -u root -p -e "RESET MASTER;"

Önemli uyarı: Binary log’ları silmeden önce mutlaka tam yedek alın. Log’ları silerseniz, o log’lardan önceki bir noktaya PITR yapma imkanınız kaybolur.

mysqlbinlog ile Remote Binary Log İşleme

Production’da bazen binlog’ları uzaktan işlemeniz gerekebilir. Özellikle disaster recovery senaryolarında:

# Remote sunucudan binary log'ları çek ve işle
mysqlbinlog --read-from-remote-server 
            --host=db-master.example.com 
            --user=replication_user 
            --password=şifre 
            --raw 
            --stop-never 
            mysql-bin.000001

--stop-never parametresi sürekli çalışarak yeni gelen log’ları da yakalar. Bu özelliği kullanarak basit bir “binary log backup” sistemi kurabilirsiniz.

MariaDB’de Farklılıklar

MariaDB, MySQL ile büyük ölçüde uyumludur ama bazı farklılıklar var:

  • log_bin MariaDB’de varsayılan olarak kapalıdır, /etc/mysql/mariadb.conf.d/50-server.cnf dosyasında açmanız gerekir
  • binlog_format MariaDB’de varsayılan olarak MIXED’dir, ROW’a çevirmeniz önerilir
  • mysqlbinlog aracı MariaDB’de mariadb-binlog olarak da çağrılabilir, ama her ikisi de çalışır
  • MariaDB 10.5+ ile BINLOG_EXPIRE_LOGS_SECONDS parametresi geldi, daha granüler kontrol sağlar
# MariaDB yapılandırması
[mysqld]
log_bin                          = /var/log/mysql/mariadb-bin.log
binlog_format                    = ROW
server_id                        = 1
expire_logs_days                 = 7
binlog_row_image                 = FULL

Monitoring ve Alerting

Binary log’ların sağlıklı çalıştığını izlemek için birkaç pratik yöntem:

#!/bin/bash
# Binary log disk kullanımını kontrol et ve uyar
BINLOG_DIR="/var/log/mysql"
THRESHOLD_GB=10
CURRENT_SIZE=$(du -sg "$BINLOG_DIR" | awk '{print $1}')

if [ "$CURRENT_SIZE" -gt "$THRESHOLD_GB" ]; then
    echo "UYARI: Binary log dizini ${CURRENT_SIZE}GB kullanıyor!" | 
    mail -s "MySQL Binlog Disk Uyarısı" [email protected]
fi

# Binary log'un aktif olduğunu kontrol et
BINLOG_STATUS=$(mysql -u root -p"$MYSQL_PASS" -e "SHOW VARIABLES LIKE 'log_bin';" 2>/dev/null | grep -c "ON")
if [ "$BINLOG_STATUS" -eq 0 ]; then
    echo "KRİTİK: Binary log devre dışı!" | 
    mail -s "MySQL Binlog Kritik Uyarı" [email protected]
fi

Prometheus ve MySQL Exporter kullanıyorsanız şu metriklere dikkat edin:

  • mysql_global_status_binlog_cache_disk_use: Binary log cache’in diske taştığını gösterir
  • mysql_global_variables_binlog_cache_size: Cache boyutunu ayarlamak için referans
  • mysql_global_status_binary_log_space: Toplam binlog disk kullanımı

Sık Yapılan Hatalar

--single-transaction olmadan yedek almak: InnoDB tablolarında tutarsız yedek alırsınız. Bu ayar olmadan PITR güvenilir olmaz.

Binlog pozisyonunu kaydetmemek: --master-data olmadan alınan yedeklerden PITR yapmak çok zorlaşır. Her zaman pozisyon bilgisini kaydedin.

Log saklama süresini çok kısa tutmak: 1-2 günlük log saklama, hafta sonu fark edilmeyen bir hatayı kurtarmanızı imkansız kılar. En az 7, idealde 14 gün saklayın.

Binary log’ları yedeklememek: Sunucu çökerse disk’teki binlog’lar da gidebilir. Binlog’ları ayrı bir lokasyona kopyalamayı ihmal etmeyin.

RESET MASTER yanlış kullanımı: Bu komut tüm binlog’ları siler. Replikasyon ortamında çalıştırırsanız slave’leri patlatırsınız.

Sonuç

Binary log ve point-in-time recovery, production MySQL/MariaDB ortamlarının sigortasıdır. Kurulumu birkaç satır config ile biten bu özellik, felaketten dakikalar içinde kurtulmanızı sağlayabilir. Ancak gerçekten işe yaraması için düzenli tam yedek alımı, binlog’ların korunması ve en önemlisi, PITR prosedürünü gerçek bir senaryoda test etmiş olmanız gerekir.

“Yedek almak değil, geri yükleyebilmek önemlidir” cümlesini binlog için de uygulayın: Test ortamınızda şimdi bir DROP TABLE yapın ve bu yazıdaki adımlarla kurtarın. Bunu bugün yapmazsanız, gerçek felakette ellerin titreyeceği garantidir.

Binary log’u açın, yedek scriptinize --master-data=2 ekleyin ve gece rahat uyuyun.

Yorum yapın