MySQL ve MariaDB kurulumlarında performans sorunlarının büyük çoğunluğu, yanlış yapılandırılmış InnoDB log ayarlarından kaynaklanır. Bir müşteri sunucusunda saatler süren yavaş yazma işlemlerini incelerken, sorunun innodb_log_file_size değerinin varsayılan 48MB’da bırakılmış olmasından kaynaklandığını gördüğümde, bu konuyu kapsamlı şekilde yazmaya karar verdim. Redo log ve undo log mekanizmalarını iyi anlamadan MySQL’i optimize etmeye çalışmak, motoru kapalı bir arabayı iterek hızlandırmaya benzer.
InnoDB Log Mekanizmasını Anlamak
InnoDB’nin dayanıklılık ve tutarlılık garantileri, iki temel log mekanizmasına dayanır: Redo Log ve Undo Log. Bu ikisini birbirine karıştırmak çok yaygın bir hata, o yüzden önce neyin ne işe yaradığını netleştirelim.
Redo Log, taahhüt edilmiş (committed) işlemlerin fiziksel değişikliklerini kaydeder. Sunucu çöktüğünde, MySQL yeniden başlarken redo log’u okuyarak henüz disk’e yazılmamış değişiklikleri uygular. Bu mekanizma sayesinde “bir işlem commit edildi mi?” sorusunun cevabı her zaman tutarlıdır.
Undo Log ise tam tersini yapar. Devam eden bir işlemin geri alınabilmesi için önceki veri versiyonlarını saklar. Ayrıca MVCC (Multi-Version Concurrency Control) mekanizmasının temelini oluşturur; yani okuma işlemleri, yazma işlemlerini bloklamadan çalışabilir çünkü okuyucular undo log üzerinden eski veri versiyonlarını görebilir.
Redo Log’un Çalışma Prensibi
Redo log, dairesel (circular) bir yapıda yazılır. MySQL 8.0’dan önce bu log, ib_logfile0 ve ib_logfile1 gibi sabit dosyalardan oluşuyordu. MySQL 8.0.30 ile birlikte redo log yönetimi tamamen değişti ve dinamik hale geldi.
# MySQL 8.0.30 öncesi redo log dosyalarını kontrol et
ls -lh /var/lib/mysql/ib_logfile*
# MySQL 8.0.30+ sonrası redo log dizini
ls -lh /var/lib/mysql/#innodb_redo/
Dairesel yapının kritik bir önemi var: log dosyası dolmaya başladığında MySQL, buffer pool’daki dirty page’leri (değiştirilmiş ama henüz diske yazılmamış sayfalar) diske yazmak zorunda kalır. Bu işleme checkpoint denir ve yoğun yazma altında sık sık checkpoint oluşmaya başlarsa ciddi performans sorunları yaşarsınız.
Redo Log Ayarları ve Optimizasyonu
innodb_log_file_size (MySQL 8.0.30 Öncesi)
Bu parametre, her bir redo log dosyasının boyutunu belirler. Toplam redo log kapasitesi innodb_log_file_size x innodb_log_files_in_group formülüyle hesaplanır.
# Mevcut ayarları görüntüle
mysql -u root -p -e "SHOW VARIABLES LIKE 'innodb_log%';"
Küçük bir redo log boyutu ne anlama gelir? Diyelim ki dakikada 200MB yazma yapan bir veritabanınız var ve log boyutunuz toplam 96MB. MySQL her 30 saniyede bir checkpoint zorlamak durumunda kalacak ve bu I/O spike’larına yol açacak. Bunu gerçek zamanlı takip etmek için şu sorguyu kullanabilirsiniz:
# Checkpoint yaşını ve log doluluğunu izle
mysql -u root -p -e "
SELECT
(innodb_os_log_written / innodb_log_file_size) AS log_writes_ratio,
innodb_log_waits
FROM information_schema.INNODB_METRICS
WHERE name IN ('log_lsn_checkpoint_age', 'innodb_log_waits');
"
# Daha pratik yöntem
mysql -u root -p -e "SHOW ENGINE INNODB STATUSG" | grep -A 10 "LOG"
Çıktıda Log sequence number ile Log flushed up to arasındaki fark büyüdükçe, sisteminiz checkpoint baskısı altında demektir.
Önerilen değerler:
- Küçük sistemler (düşük yazma yükü): 256MB – 512MB
- Orta ölçekli üretim sunucuları: 1GB – 2GB
- Yoğun yazma yükü olan sistemler: 4GB – 8GB veya üzeri
# /etc/mysql/mysql.conf.d/mysqld.cnf veya /etc/my.cnf dosyasını düzenle
sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
[mysqld]
# MySQL 8.0.30 öncesi için
innodb_log_file_size = 2G
innodb_log_files_in_group = 2
# Bu ayarları değiştirmek için MySQL'i durdurman gerekir
# Eski log dosyalarını sil ve MySQL'i yeniden başlat
Log boyutunu değiştirmek için şu adımları izle:
# 1. MySQL'i durdur
sudo systemctl stop mysql
# 2. Eski log dosyalarını yedekle ve sil
sudo mv /var/lib/mysql/ib_logfile0 /tmp/ib_logfile0.bak
sudo mv /var/lib/mysql/ib_logfile1 /tmp/ib_logfile1.bak
# 3. my.cnf'yi düzenle (yukarıdaki gibi)
# 4. MySQL'i başlat - yeni log dosyaları otomatik oluşur
sudo systemctl start mysql
# 5. Yeni dosyaları doğrula
ls -lh /var/lib/mysql/ib_logfile*
innodb_log_buffer_size
Redo log buffer, commit edilmeden önce log kayıtlarının bellekte tutulduğu alandır. Büyük işlemler (batch insert, bulk update) için bu değerin artırılması disk I/O’sunu önemli ölçüde azaltır.
# Mevcut buffer kullanımını kontrol et
mysql -u root -p -e "
SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';
SHOW GLOBAL STATUS LIKE 'Innodb_os_log_written';
"
Innodb_log_waits değeri sıfırdan büyükse, buffer yetersiz demektir. Genellikle 64MB ile 256MB arası yeterli olur. Çok büyük transaction’larla çalışıyorsanız 512MB’a kadar çıkabilirsiniz.
innodb_flush_log_at_trx_commit
Bu parametre, performans ile veri güvenliği arasındaki en kritik tradeoff’u belirler.
- 1 (varsayılan): Her commit’te log buffer diske flush edilir. En güvenli seçenek, ACID uyumlu. Yazma performansı en düşük.
- 2: Her commit’te log OS buffer’a yazılır, saniyede bir diske flush edilir. Sunucu çökerse en fazla 1 saniyelik veri kaybı olabilir.
- 0: Saniyede bir hem OS buffer’a yazar hem flush eder. MySQL crash’inde 1 saniyeye kadar veri kaybı riski.
# Raporlama veritabanları veya veri kaybının tolere edildiği durumlar için
[mysqld]
innodb_flush_log_at_trx_commit = 2
# Kritik finansal veriler için kesinlikle 1 kullan
innodb_flush_log_at_trx_commit = 1
E-ticaret projelerinde sıkça şu senaryoyla karşılaşıyorum: Geliştiriciler performans için değeri 0’a çekiyor, test ortamında mükemmel çalışıyor, üretimde beklenmedik bir server restart sonrası birkaç dakikalık sipariş verisi kayboluyor. Dengeyi iyi kur.
MySQL 8.0.30+ Dinamik Redo Log Yönetimi
MySQL 8.0.30 ile birlikte redo log artık dinamik olarak büyüyüp küçülebiliyor. Bu büyük bir gelişme.
# Yeni sistem için parametreler
[mysqld]
innodb_redo_log_capacity = 8G
# Çalışırken değiştirme (restart gerektirmez!)
mysql -u root -p -e "SET GLOBAL innodb_redo_log_capacity = 4294967296;" # 4GB
# Redo log durumunu kontrol et
mysql -u root -p -e "SELECT * FROM performance_schema.innodb_redo_log_files;"
Undo Log Ayarları ve Optimizasyonu
Undo log, InnoDB’nin en az anlaşılan bileşenlerinden biri. Özellikle uzun süren transaction’lar ve yoğun MVCC kullanımında undo log büyümesi ciddi sorunlara yol açabilir.
Undo Tablespace Yönetimi
MySQL 5.6 öncesinde undo log, sistem tablespace’i (ibdata1) içindeydi ve bir kez büyüdüğünde küçülmesi mümkün değildi. Bu, pek çok DBA’in kabusu olan “şişmiş ibdata1” sorunuydu.
# Mevcut undo tablespace'leri listele
mysql -u root -p -e "SELECT * FROM information_schema.INNODB_TABLESPACES WHERE name LIKE 'undo%';"
# Undo tablespace boyutlarını kontrol et
mysql -u root -p -e "
SELECT
space_id,
name,
ROUND(file_size/1024/1024, 2) AS size_mb,
state
FROM information_schema.INNODB_TABLESPACES
WHERE name LIKE '%undo%';
"
MySQL 8.0 ile birlikte ayrı undo tablespace dosyaları oluşturulabiliyor ve en önemlisi bu dosyalar truncate edilebiliyor, yani küçültülebiliyor.
# Undo tablespace yapılandırması
[mysqld]
innodb_undo_tablespaces = 2 # Önerilen minimum
innodb_undo_log_truncate = ON # Otomatik küçültme
innodb_max_undo_log_size = 1G # Bu boyutu geçince truncate tetiklenir
innodb_purge_rseg_truncate_frequency = 128
Undo Log Şişmesini Tespit Etmek
Uzun süren transaction’lar, undo log’un büyümesine neden olur. Çünkü transaction devam ettiği sürece o transaction’dan önceki veri versiyonları silinemiyor.
# Uzun süren transaction'ları tespit et
mysql -u root -p -e "
SELECT
trx_id,
trx_state,
trx_started,
TIMESTAMPDIFF(SECOND, trx_started, NOW()) AS duration_seconds,
trx_rows_modified,
trx_rows_locked,
LEFT(trx_query, 100) AS query_snippet
FROM information_schema.INNODB_TRX
ORDER BY trx_started ASC;
"
Eğer burada 3600 saniye (1 saat) boyunca çalışan bir transaction görüyorsan, undo log o süre boyunca büyümeye devam etmiş demektir. Bu sık karşılaştığım bir senaryo: gecenin köründe başlayan ve sabaha kadar tamamlanamayan bulk import işlemleri.
# History list length'i izle (undo log birikimi göstergesi)
mysql -u root -p -e "SHOW ENGINE INNODB STATUSG" | grep "History list length"
# Performance Schema üzerinden izleme
mysql -u root -p -e "
SELECT name, count
FROM performance_schema.global_status
WHERE variable_name = 'innodb_history_list_length';
"
History list length değeri normalda 0-1000 arasında olmalı. 10.000’i geçmeye başlarsa alarm ver, 100.000’in üzerindeyse ciddi bir sorun var demektir.
Purge Thread Optimizasyonu
InnoDB, undo log kayıtlarını temizlemek için purge thread’leri kullanır. Bu thread’lerin sayısını artırmak, undo log birikimini önlemeye yardımcı olur.
[mysqld]
innodb_purge_threads = 4 # Varsayılan 4, yoğun sistemlerde 8'e çıkarılabilir
innodb_purge_batch_size = 300 # Her purge döngüsünde işlenecek undo kayıt sayısı
# Purge işleminin durumunu izle
mysql -u root -p -e "
SELECT name, count
FROM performance_schema.global_status
WHERE variable_name LIKE '%purge%';
"
Gerçek Dünya Senaryosu: E-Ticaret Platformu Optimizasyonu
Bir e-ticaret platformunda şu sorunla karşılaştım: sipariş oluşturma işlemi peak saatlerde 5-8 saniye sürüyordu. Sistemin profili şöyleydi:
- 64 çekirdekli sunucu, 256GB RAM
- NVMe SSD RAID10
- MySQL 8.0.28
- innodb_log_file_size: 48MB (varsayılan!)
- innodb_buffer_pool_size: 128GB (düzgün ayarlanmış)
- Dakikada ~50.000 sipariş işlemi
Teşhis adımları:
# Performans darboğazını tespit et
mysql -u root -p -e "
SELECT
event_name,
count_star,
sum_timer_wait/1000000000000 as wait_seconds,
avg_timer_wait/1000000000 as avg_ms
FROM performance_schema.events_waits_summary_global_by_event_name
WHERE event_name LIKE '%log%'
ORDER BY sum_timer_wait DESC
LIMIT 10;
"
Çıktı açıktı: wait/io/file/innodb/innodb_log_file bekleme süresi toplam bekleme sürelerinin %60’ını oluşturuyordu. Log dosyası o kadar küçüktü ki, sistem sürekli checkpoint yapıyordu.
Uygulanan çözüm:
[mysqld]
# Redo log boyutunu dramatik şekilde artır
innodb_log_file_size = 4G
innodb_log_files_in_group = 2
innodb_log_buffer_size = 256M
# Flush stratejisini güncelle (finansal işlem olduğu için 1'de kaldı)
innodb_flush_log_at_trx_commit = 1
# Undo log optimizasyonu
innodb_undo_log_truncate = ON
innodb_max_undo_log_size = 2G
innodb_purge_threads = 8
# Checkpoint agresifliğini azalt
innodb_io_capacity = 4000
innodb_io_capacity_max = 8000
innodb_adaptive_flushing = ON
Değişiklik sonrasında sipariş oluşturma süresi ortalama 0.3 saniyeye düştü. Aynı donanım, aynı kod, sadece log konfigürasyonu değişti.
İzleme ve Alarm Kurulumu
Doğru yapılandırma kadar önemli olan şey, sürekli izlemedir. Birkaç kritik metriği düzenli olarak takip etmeni öneririm:
#!/bin/bash
# innodb_log_monitor.sh - Cron ile çalıştır
MYSQL_USER="monitor"
MYSQL_PASS="şifre"
THRESHOLD_HISTORY=10000
THRESHOLD_LOG_WAIT=100
HISTORY_LEN=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -N -e "
SELECT count FROM performance_schema.global_status
WHERE variable_name = 'innodb_history_list_length';
" 2>/dev/null)
LOG_WAITS=$(mysql -u$MYSQL_USER -p$MYSQL_PASS -N -e "
SHOW GLOBAL STATUS LIKE 'Innodb_log_waits';
" 2>/dev/null | awk '{print $2}')
if [ "$HISTORY_LEN" -gt "$THRESHOLD_HISTORY" ]; then
echo "UYARI: InnoDB history list length kritik seviyede: $HISTORY_LEN" |
mail -s "MySQL Undo Log Uyarısı" [email protected]
fi
if [ "$LOG_WAITS" -gt "$THRESHOLD_LOG_WAIT" ]; then
echo "UYARI: InnoDB log wait sayısı yüksek: $LOG_WAITS" |
mail -s "MySQL Redo Log Uyarısı" [email protected]
fi
# Cron'a ekle (5 dakikada bir çalıştır)
echo "*/5 * * * * /usr/local/bin/innodb_log_monitor.sh" | crontab -
Sık Yapılan Hatalar
Varsayılan ayarları üretimde kullanmak: MySQL varsayılan değerleri, geliştirme ve test ortamları için tasarlanmıştır. 48MB redo log, modern bir üretim sisteminde yetersiz kalır.
ibdata1 içinde undo log bırakmak: MySQL 5.7 ve üzerinde kesinlikle ayrı undo tablespace kullanmaya geçmelisin. ibdata1 büyüdüğünde küçültemezsin.
innodb_flush_log_at_trx_commit’i gelişigüzel değiştirmek: Değeri 0 yapmak yazma hızını artırır ama bir sonraki beklenmedik kapanmada veri kaybı yaşarsın. Uygulamanın tolerans seviyesine göre karar ver.
Log boyutunu artırdıktan sonra recovery süresini göz ardı etmek: Çok büyük redo log dosyaları, crash recovery sırasında çok uzun sürebilir. 8GB’ın üzerindeki değerler için recovery testini mutlaka yap.
Uzun süren transaction’ları görmezden gelmek: Undo log şişmesinin birincil nedeni budur. Periyodik transaction monitoring kurmazsan, bir gün diski dolmuş bulursun.
Sonuç
InnoDB redo log ve undo log ayarları, MySQL performansının ve güvenilirliğinin temel taşlarıdır. Redo log boyutunu doğru ayarlamak checkpoint baskısını ortadan kaldırır ve yazma performansını ciddi ölçüde artırır. Undo log yönetimi ise hem disk kullanımını kontrol altında tutar hem de uzun süren transaction’ların yarattığı MVCC yükünü azaltır.
Önerilen yaklaşım şu sırayla ilerlemektir: önce mevcut sistemi izle ve darboğazları tespit et, sonra küçük değişikliklerle denemeler yap, ardından izlemeye devam et. Her sistem farklıdır; burada verilen değerler iyi bir başlangıç noktasıdır ama kendi workload’ına göre ince ayar yapman gerekir.
MySQL 8.0.30 ve sonrasında gelen dinamik redo log yönetimi, bu konuyu çok daha kolay hale getirdi. Eski sürümlerdeysen mümkün olan en kısa sürede güncelleme planı yapmani tavsiye ederim. Hem yönetim kolaylığı hem de performans açısından fark büyük.
Son olarak şunu söyleyeyim: bu ayarları değiştirmeden önce mutlaka yedek al ve değişikliği önce bir test ortamında dene. Redo log dosyasını silerken MySQL’in kapalı olduğundan emin ol; aksi halde veri kaybı yaşayabilirsin.