MySQL Binary Log Şişmesi: Temizleme ve Yönetim Rehberi

Sunucuya gece yarısı bağlandığınızda disk kullanımının %95’e ulaştığını görüyorsunuz ve df -h çıktısında /var/lib/mysql dizininin korkunç boyutlara şiştiğini fark ediyorsunuz. Klasörü incelediğinizde onlarca, belki yüzlerce mysql-bin.000XXX dosyasıyla karşılaşıyorsunuz. Binary log şişmesi, MySQL yöneticilerinin en sık karşılaştığı ve en çok zaman kaybettiren sorunlardan biridir. Bu yazıda konuyu baştan sona ele alacağız.

Binary Log Nedir ve Neden Şişer?

MySQL binary log (binlog), veritabanında gerçekleşen tüm değişiklikleri kaydeden bir günlük sistemidir. INSERT, UPDATE, DELETE gibi DML işlemleri ve şema değişiklikleri bu dosyalara yazılır. Replikasyon yapılandırmalarında slave sunucuların master’ı takip etmesi, point-in-time recovery işlemleri ve audit amaçlı kullanım için kritik öneme sahiptir.

Peki neden şişer? Birkaç temel sebep vardır:

  • expire_logs_days veya binlog_expire_logs_seconds parametresi ya çok yüksek ayarlanmıştır ya da hiç ayarlanmamıştır
  • Replikasyon slave’i durmuştur ve master binary logları silemez
  • Uygulama tarafında büyük toplu işlemler (bulk insert/update) gerçekleştirilmektedir
  • Row-based logging modunda büyük tablo güncellemeleri yapılmaktadır
  • Manuel temizleme hiç yapılmamıştır

Özellikle replikasyon ortamlarında master, slave’in hangi log pozisyonunda olduğunu takip eder ve slave’in okumadığı logları silmez. Slave sunucu ağ sorunu, crash veya yanlış yapılandırma nedeniyle duraklamışsa master üzerinde loglar birikmeye devam eder.

Mevcut Durumu Analiz Etmek

Temizliğe başlamadan önce mevcut durumu anlamak şarttır. Aceleyle yanlış dosyaları silerseniz replikasyon bozulur veya veri kurtarma imkânı ortadan kalkar.

# Disk kullanımını kontrol et
df -h /var/lib/mysql

# Binary log dosyalarının toplam boyutunu öğren
du -sh /var/lib/mysql/mysql-bin.*

# Dosya sayısını say
ls /var/lib/mysql/mysql-bin.* | wc -l

# En büyük 10 binary log dosyasını listele
ls -lSh /var/lib/mysql/mysql-bin.* | head -10

MySQL içinden de durum incelemesi yapmalısınız:

-- Mevcut binary log listesi ve boyutları
SHOW BINARY LOGS;

-- Master status bilgisi (hangi log, hangi pozisyon)
SHOW MASTER STATUS;

-- Replikasyon varsa slave durumunu kontrol et
SHOW SLAVE STATUSG

-- Binary log ile ilgili sistem değişkenlerini kontrol et
SHOW VARIABLES LIKE '%binlog%';
SHOW VARIABLES LIKE '%expire%';
SHOW VARIABLES LIKE 'log_bin%';

SHOW SLAVE STATUSG çıktısında dikkat etmeniz gereken kritik alanlar:

  • Slave_IO_Running: IO thread’inin çalışıp çalışmadığı
  • Slave_SQL_Running: SQL thread’inin çalışıp çalışmadığı
  • Seconds_Behind_Master: Slave’in ne kadar geride olduğu
  • Master_Log_File ve Read_Master_Log_Pos: Slave’in hangi noktada olduğu
  • Last_Error: Varsa son hata mesajı

Eğer her iki thread de Yes değerindeyse ve Seconds_Behind_Master makul bir sayıdaysa replikasyon sağlıklı demektir. Bu durumda güvenli temizlik yapabilirsiniz.

Binary Log Temizleme Yöntemleri

Manuel Temizlik: PURGE BINARY LOGS

En kontrollü yöntem, MySQL’in kendi PURGE BINARY LOGS komutunu kullanmaktır. Bu komut, belirttiğiniz noktaya kadar olan tüm binary logları güvenli şekilde siler.

-- Belirli bir tarihe kadar olan logları sil
PURGE BINARY LOGS BEFORE '2024-01-15 00:00:00';

-- Belirli bir log dosyasına kadar olanları sil (o dosya dahil değil)
PURGE BINARY LOGS TO 'mysql-bin.001250';

-- Bugünden 7 gün öncesine kadar olanları sil
PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 7 DAY);

Önemli not: Replikasyon ortamında PURGE BINARY LOGS komutu slave’in kullandığı logları silmez, bu nedenle oldukça güvenlidir. Ancak slave durmuşsa bu koruma çalışmaz. Bu yüzden her zaman önce SHOW SLAVE STATUSG ile durumu kontrol edin.

RESET MASTER: Nükleer Seçenek

RESET MASTER tüm binary logları siler ve sıfırdan başlar. Replikasyon ortamında kesinlikle kullanmayın. Standalone bir sunucuda disk dolu kriz anında işe yarar ama veri kurtarma imkânınızı da ortadan kaldırır.

-- TÜM binary logları sil ve sıfırla (replikasyon ortamında KULLANMAYIN)
RESET MASTER;

Otomatik Temizlik: expire_logs_days

Kalıcı çözüm için expire_logs_days veya MySQL 8.0+ için binlog_expire_logs_seconds parametresini ayarlamalısınız:

-- Anlık olarak ayarla (restart gerektirmez)
SET GLOBAL expire_logs_days = 7;

-- MySQL 8.0+ için saniye cinsinden
SET GLOBAL binlog_expire_logs_seconds = 604800;  -- 7 gün

-- Mevcut değeri kontrol et
SHOW VARIABLES LIKE 'expire_logs_days';
SHOW VARIABLES LIKE 'binlog_expire_logs_seconds';

Bu değişikliğin kalıcı olması için my.cnf dosyasına da eklemeniz gerekir:

[mysqld]
# MySQL 5.7 için
expire_logs_days = 7

# MySQL 8.0+ için (her ikisini birden yazmayın, 8.0'da expire_logs_days deprecated)
binlog_expire_logs_seconds = 604800

# Binary log boyut limiti
max_binlog_size = 100M

Gerçek Dünya Senaryosu: Kriz Anında Disk Kurtarma

Senaryo: Sabah 03:00’te alarm geliyor. Prod MySQL sunucusunda disk %98 dolmuş, yazma işlemleri hata veriyor. Binary loglar 200 GB yer kaplamış.

# Önce hangi process'in ne kadar I/O yaptığını gör
iotop -ao

# MySQL disk kullanımını hızlıca analiz et
du -sh /var/lib/mysql/*bin* 2>/dev/null | sort -rh | head -20

# Acil olarak ne kadar yer kazanılabilir?
ls /var/lib/mysql/mysql-bin.0* | wc -l
ls -lh /var/lib/mysql/mysql-bin.0* | tail -5
-- MySQL'e bağlan ve durumu değerlendir
SHOW MASTER STATUS;
SHOW SLAVE STATUSG

-- Slave çalışıyorsa ve o anki master log dosyası mysql-bin.002150 ise
-- 2100'e kadar olanları güvenle silebiliriz
PURGE BINARY LOGS TO 'mysql-bin.002100';

-- Ardından otomatik temizliği aktif et
SET GLOBAL expire_logs_days = 5;

Bu adımların ardından disk kullanımı hemen düşecektir. Ancak kriz geçtikten sonra kalıcı çözümü uygulamayı ihmal etmeyin.

Replikasyon Ortamında Güvenli Temizlik

Replikasyon olan bir ortamda binary log temizliği çok daha dikkatli yapılmalıdır. Birden fazla slave varsa hepsinin durumunu kontrol etmelisiniz.

-- Master sunucuda tüm slave'lerin hangi logu okuduğunu öğren
-- Her slave'de çalıştırın:
SHOW SLAVE STATUSG

-- Slave'lerin Master_Log_File değerlerini not alın
-- En geriden gelen slave'in pozisyonu güvenli silme sınırınızdır

-- Örneğin en yavaş slave mysql-bin.001890'ı okuyorsa
-- 1889 ve öncesini silebiliriz
PURGE BINARY LOGS TO 'mysql-bin.001889';

Birden fazla slave olan ortamlar için otomatik kontrol scripti kullanmak çok daha pratiktir:

#!/bin/bash
# check_slave_positions.sh
# Tüm slave'lerin pozisyonlarını kontrol edip güvenli temizlik noktasını belirle

MASTER_HOST="master-db.example.com"
MYSQL_USER="repl_monitor"
MYSQL_PASS="guclu_sifre"

SLAVE_HOSTS=("slave1.example.com" "slave2.example.com" "slave3.example.com")

echo "=== Master Binary Log Durumu ==="
mysql -h $MASTER_HOST -u $MYSQL_USER -p$MYSQL_PASS -e "SHOW MASTER STATUS;"

echo ""
echo "=== Slave Pozisyonları ==="

for slave in "${SLAVE_HOSTS[@]}"; do
    echo "--- $slave ---"
    mysql -h $slave -u $MYSQL_USER -p$MYSQL_PASS -e 
        "SHOW SLAVE STATUSG" 2>/dev/null | 
        grep -E "Master_Log_File|Read_Master_Log_Pos|Slave_IO_Running|Slave_SQL_Running|Seconds_Behind_Master"
    echo ""
done

Monitoring ve Proaktif Yönetim

Kriz oluştuktan sonra değil, oluşmadan önce müdahale etmek için izleme sistemi kurmalısınız.

#!/bin/bash
# binlog_monitor.sh
# Binary log boyutunu izle ve uyar

MYSQL_USER="monitor"
MYSQL_PASS="monitor_pass"
THRESHOLD_GB=50
ALERT_EMAIL="[email protected]"

# Binary log toplam boyutunu hesapla (MB cinsinden)
BINLOG_SIZE=$(mysql -u $MYSQL_USER -p$MYSQL_PASS -N -e 
    "SELECT ROUND(SUM(file_size)/1024/1024/1024, 2) 
     FROM information_schema.FILES 
     WHERE file_type='UNDO LOG'" 2>/dev/null)

# Alternatif: disk üzerinden hesapla
BINLOG_SIZE_DISK=$(du -sm /var/lib/mysql/mysql-bin.* 2>/dev/null | 
    awk '{sum+=$1} END {print int(sum/1024)}')

echo "Binary log toplam boyutu: ${BINLOG_SIZE_DISK} GB"

if [ "$BINLOG_SIZE_DISK" -gt "$THRESHOLD_GB" ]; then
    echo "UYARI: Binary log boyutu ${BINLOG_SIZE_DISK}GB eşiği aştı!" | 
        mail -s "[ALARM] MySQL Binary Log Şişmesi - $(hostname)" $ALERT_EMAIL
    
    # Otomatik temizlik için MySQL'e bağlan
    mysql -u $MYSQL_USER -p$MYSQL_PASS -e 
        "PURGE BINARY LOGS BEFORE DATE_SUB(NOW(), INTERVAL 3 DAY);"
    
    echo "Otomatik temizlik yapıldı - $(date)" >> /var/log/mysql_binlog_cleanup.log
fi

Bu scripti cron’a ekleyin:

# Her 4 saatte bir çalıştır
echo "0 */4 * * * root /usr/local/bin/binlog_monitor.sh" >> /etc/cron.d/mysql-binlog-monitor

Binary Log Boyutunu Azaltmak İçin Yapılandırma Optimizasyonu

Şişmeyi reaktif olarak değil, proaktif olarak önlemek için yapılandırmayı optimize etmek gerekir.

[mysqld]
# Binary log aktif (replikasyon veya PITR gerekmiyorsa kapatabilirsiniz)
log_bin = /var/lib/mysql/mysql-bin

# Maksimum tek dosya boyutu
max_binlog_size = 100M

# MySQL 8.0+ için otomatik temizlik süresi (saniye)
binlog_expire_logs_seconds = 432000  # 5 gün

# Logging formatı: ROW, STATEMENT veya MIXED
# STATEMENT genellikle daha az yer kaplar ama bazı işlemler için güvensiz
# ROW daha güvenli ama daha fazla yer kaplar
binlog_format = ROW

# ROW format kullanıyorsanız sadece değişen sütunları logla
binlog_row_image = MINIMAL

# Büyük transaction'ları disk yerine bellekte tut (performans için)
binlog_cache_size = 1M
max_binlog_cache_size = 512M

# Sync politikası: 1=en güvenli ama yavaş, 0=en hızlı ama riskli
sync_binlog = 1

binlog_row_image = MINIMAL ayarı özellikle dikkat çekmeye değer. Varsayılan FULL değeri her satır güncellemesinde tüm sütun verilerini kaydeder. MINIMAL sadece PRIMARY KEY ve değişen sütunları kaydeder. Büyük tablolarda bu fark binary log boyutunu %60-70 oranında azaltabilir.

Binlog Format Seçimi ve Boyut Etkisi

Üç farklı binary log formatı vardır ve her birinin boyut üzerinde farklı etkisi vardır:

  • STATEMENT: SQL ifadelerini olduğu gibi kaydeder. En az yer kaplar ancak NOW(), RAND() gibi non-deterministic fonksiyonlarda tutarsızlık yaratabilir
  • ROW: Her satır değişikliğini ayrı ayrı kaydeder. En fazla yer kaplar ama en güvenilir yöntemdir. Özellikle UPDATE table SET status=1 gibi toplu güncellemelerde devasa log dosyaları oluşturabilir
  • MIXED: Mümkün olduğunda STATEMENT, güvensiz durumlarda ROW kullanır. Orta yol çözümüdür
-- Mevcut format nedir?
SHOW VARIABLES LIKE 'binlog_format';

-- Format değiştir (yeni session'lar için)
SET GLOBAL binlog_format = 'MIXED';

-- Belirli bir işlem için geçici olarak değiştir
SET SESSION binlog_format = 'STATEMENT';

Binary Logları Analiz Etmek

Şişmeye neden olan işlemleri tespit etmek için mysqlbinlog aracını kullanabilirsiniz:

# Belirli bir zaman aralığındaki işlemleri gör
mysqlbinlog --start-datetime="2024-01-15 10:00:00" 
            --stop-datetime="2024-01-15 11:00:00" 
            /var/lib/mysql/mysql-bin.002100

# Belirli bir veritabanının işlemlerini filtrele
mysqlbinlog --database=production_db 
            /var/lib/mysql/mysql-bin.002100 | grep -E "^(INSERT|UPDATE|DELETE)" | wc -l

# En büyük transaction'ları bul
mysqlbinlog /var/lib/mysql/mysql-bin.002100 | 
    awk '/^# at /{pos=$3} /^COMMIT/{print NR, pos}' | 
    sort -k2 -rn | head -20

# Binary log içeriğini daha okunabilir formatta incele
mysqlbinlog --verbose --base64-output=DECODE-ROWS 
            /var/lib/mysql/mysql-bin.002100 | less

Point-in-Time Recovery İçin Binary Log Yönetimi

Binary logların en kritik kullanım amaclarından biri point-in-time recovery’dir. Bu nedenle silmeden önce neleri gözden geçirmeniz gerektiğini bilmek önemlidir.

# Son full backup'tan bu yana olan logları listeleme
# Diyelim ki son backup 2024-01-10 02:00:00'da alındı
mysqlbinlog --start-datetime="2024-01-10 02:00:00" 
            /var/lib/mysql/mysql-bin.* | 
            grep "^# at" | wc -l

# Recovery senaryosu: yanlışlıkla silinen tabloyu geri getir
# 1. Full backup'ı geri yükle
# 2. Silme işleminden hemen öncesine kadar logları uygula
mysqlbinlog --start-datetime="2024-01-10 02:00:00" 
            --stop-datetime="2024-01-15 14:32:00" 
            /var/lib/mysql/mysql-bin.002100 
            /var/lib/mysql/mysql-bin.002101 | 
            mysql -u root -p production_db

Bu senaryo göz önünde bulundurulduğunda, backup aldıktan sonraki logları en az backup retention süreniz kadar tutmanız önerilir. Günlük backup alıyorsanız 7-14 gün binary log saklamak mantıklı bir denge sağlar.

Logları MySQL Dışına Arşivleme

Disk doluysa ama logları silmek istemiyorsanız, eski logları başka bir konuma taşıyabilirsiniz:

#!/bin/bash
# archive_old_binlogs.sh
# Eski binary logları S3'e arşivle ve yerel dosyaları sil

MYSQL_DATA="/var/lib/mysql"
ARCHIVE_BUCKET="s3://sirket-mysql-backups/binlogs"
KEEP_DAYS=7
HOSTNAME=$(hostname -s)

# 7 günden eski binary logları bul ve sıkıştır
find $MYSQL_DATA -name "mysql-bin.*" -mtime +$KEEP_DAYS | 
while read logfile; do
    filename=$(basename $logfile)
    
    # Sıkıştır ve S3'e yükle
    gzip -c $logfile | aws s3 cp - 
        "${ARCHIVE_BUCKET}/${HOSTNAME}/${filename}.gz" 
        --storage-class STANDARD_IA
    
    if [ $? -eq 0 ]; then
        echo "Arşivlendi: $filename"
        # MySQL üzerinden güvenli silme
        mysql -u root -p"$MYSQL_ROOT_PASS" -e 
            "PURGE BINARY LOGS TO '$filename';"
    else
        echo "HATA: $filename arşivlenemedi!"
    fi
done

echo "Arşivleme tamamlandı: $(date)" >> /var/log/binlog_archive.log

Sonuç

Binary log şişmesi, önceden önlem alınmazsa prod ortamı felç eden bir sorun olmaya devam edecektir. Bu yazıda ele aldığımız adımları kısaca özetlemek gerekirse:

  • Anlık kriz durumunda: PURGE BINARY LOGS BEFORE veya PURGE BINARY LOGS TO komutlarıyla güvenli temizlik yapın, replikasyon varsa mutlaka slave pozisyonunu kontrol edin
  • Kalıcı çözüm için: expire_logs_days veya binlog_expire_logs_seconds parametrelerini my.cnf‘e ekleyin ve yeniden başlatın
  • Boyutu azaltmak için: binlog_row_image = MINIMAL ve uygun binlog_format ayarlarını yapılandırın
  • Proaktif yönetim için: Disk kullanımını izleyen ve otomatik temizlik yapan bir script kurun, bunu cron ile düzenli çalıştırın
  • Arşivleme için: Eski logları silmeden önce uzak depolama alanına taşıyın, point-in-time recovery ihtiyacına göre retention policy belirleyin

En sık yapılan hata, binary log yönetimini “gerektiğinde hallederiz” zihniyetiyle ertelemektir. Bir kez monitoring kurulduğunda ve my.cnf doğru yapılandırıldığında bu sorun neredeyse tamamen ortadan kalkar. Beş dakika ayırıp bu yapılandırmaları yapmanız, sizi gece yarısı kriz anında saatlerce uğraşmaktan kurtaracaktır.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir