Bozuk MySQL Tablosu Nasıl Onarılır: myisamchk ve innodb_force_recovery Rehberi
Sabah 3’te telefonun çaldığını, müşterinin “site tamamen çöktü, veritabanı hatası veriyor” diye bağırdığını hayal et. MySQL loglarına bakıyorsun, karşında şu satır duruyor:
[ERROR] MySQL: Table './mydb/orders' is marked as crashed and should be repaired
İşte tam bu an için bu yazıyı yazıyorum. MyISAM ve InnoDB tablolarında bozulma gerçek bir problem ve doğru araçları bilmeden bu durumdan çıkmak oldukça stresli olabiliyor. myisamchk ve innodb_force_recovery parametrelerini doğru kullanmayı öğrendiğinde, bu tür krizleri saatler içinde değil dakikalar içinde çözebilirsin.
Tablo Bozulması Neden Olur?
Önce düşmanı tanıyalım. MySQL tablolarında bozulma genellikle şu sebeplerden kaynaklanır:
- Ani güç kesintisi veya sistem çökmesi: Yazma işlemi ortasında sistem kapanırsa veri tutarsızlığı kaçınılmaz olur
- Disk hatası: Bad sector’lar veya RAID bozulması
- MySQL process’inin zorla sonlandırılması:
kill -9veya OOM killer devreye girdiğinde - Dosya sistemi hataları: ext4 veya xfs üzerinde journal hatalarından sonra
- Donanım sorunu: RAM hataları bazen veri yazımını bozar
- Yetersiz disk alanı: Yazma işlemi yarıda kalır
MyISAM ve InnoDB bu sorunlara farklı tepki verir. MyISAM dosya tabanlı ve daha kırılgandır; crash recovery mekanizması zayıftır. InnoDB ise transaction log kullanır ve genellikle kendini otomatik onarır, ama ciddi hasarlarda yine de müdahale gerekir.
MyISAM Tablo Onarımı: myisamchk
myisamchk Nedir ve Nasıl Çalışır?
myisamchk, MyISAM depolama motorunu kullanan tablolar için komut satırı onarım aracıdır. MySQL kurulumunun bir parçası olarak gelir ve doğrudan .MYI (index) ve .MYD (data) dosyaları üzerinde çalışır.
Kritik uyarı: myisamchk‘yi çalıştırmadan önce MySQL servisini durdurman veya ilgili tabloları kilitlemen gerekir. Aksi takdirde MySQL ve myisamchk aynı anda dosyalara yazabilir ve durumu daha da kötüleştirebilirsin.
Temel Kontrol ve Onarım Komutları
Önce durumu görmek için kontrol komutu:
# Tüm tabloları kontrol et
myisamchk /var/lib/mysql/mydb/*.MYI
# Belirli bir tabloyu kontrol et
myisamchk /var/lib/mysql/mydb/orders.MYI
# Detaylı çıktı için verbose mod
myisamchk -v /var/lib/mysql/mydb/orders.MYI
Kontrol çıktısında şöyle bir şey görürsen tablo bozuktur:
myisamchk: error 126: Incorrect key file for table: 'orders'. Try to repair it
MyISAM-table 'orders.MYI' is corrupted
Fix it using switch "-r" or "-o"
Şimdi onarıma geçelim:
# MySQL'i durdur
systemctl stop mysql
# Standart onarım (-r: recover mode)
myisamchk -r /var/lib/mysql/mydb/orders.MYI
# Daha agresif onarım (-o: safe recover, eski yöntem ama güvenilir)
myisamchk -o /var/lib/mysql/mydb/orders.MYI
# En kapsamlı onarım seçeneği
myisamchk -r -v --update-state /var/lib/mysql/mydb/orders.MYI
myisamchk Parametreleri
Sık kullandığım ve bilmen gereken parametreler:
-r (–recover): Standart recovery modu. Index dosyasını sıfırdan oluşturur, silinen satırları temizler. Çoğu durumda bu yeterli.
-o (–safe-recover): Satır satır okuyarak yeniden oluşturur. Çok yavaş ama -r işe yaramazsa dene.
-e (–extend-check): Çok kapsamlı kontrol, her key için her satırı doğrular. Yavaş ama kesin sonuç verir.
-f (–force): Geçici dosyalar varsa bile çalışmaya devam eder.
-v (–verbose): Detaylı çıktı, ne yaptığını görmek için şart.
–update-state: Kontrol sonucunu tablonun içine yazar, sonraki CHECK TABLE komutlarında kullanılır.
-U (–unpack): Pack edilmiş tabloları açar.
-q (–quick): Sadece index’i kontrol eder, data dosyasına dokunmaz. Hızlıdır ama incomplete olabilir.
Gerçek Dünya Senaryosu: Büyük MyISAM Tablosunda Bozulma
Diyelim ki 50GB’lık bir MyISAM log tablosun var ve bozuldu. Standart yöntem çok uzun sürecek. Şu sıralamayı takip et:
# Önce backup al (kritik!)
cp -a /var/lib/mysql/mydb/logs.MYD /backup/logs.MYD.bak
cp -a /var/lib/mysql/mydb/logs.MYI /backup/logs.MYI.bak
# MySQL servisini durdur
systemctl stop mysql
# Önce hızlı kontrol
myisamchk -v /var/lib/mysql/mydb/logs.MYI
# Standart onarım dene
myisamchk -r -v /var/lib/mysql/mydb/logs.MYI
# Eğer yukarıdaki başarısız olursa:
myisamchk -o -v /var/lib/mysql/mydb/logs.MYI
# MySQL'i yeniden başlat
systemctl start mysql
# Onarım sonrası doğrulama
mysql -u root -p -e "CHECK TABLE mydb.logs;"
MySQL İçinden Onarım
Sunucuyu durduramıyorsan, MySQL içinden de onarabilirsin:
mysql -u root -p
# Tabloyu kontrol et
mysql> CHECK TABLE mydb.orders;
# Standart onarım
mysql> REPAIR TABLE mydb.orders;
# Daha kapsamlı onarım
mysql> REPAIR TABLE mydb.orders EXTENDED;
# Quick onarım (sadece index)
mysql> REPAIR TABLE mydb.orders QUICK;
REPAIR TABLE komutu, sunucu ayaktayken çalışır ama tablo üzerinde write lock açar. Yani diğer sorgular beklemek zorunda kalır. Büyük tablolarda bu ciddi bir kesintiye yol açabilir, önceden plan yap.
InnoDB Tablo Onarımı: innodb_force_recovery
InnoDB Neden Farklı?
InnoDB, MyISAM’dan çok daha sağlam bir yapıya sahip. ACID uyumludur, transaction log (redo log) kullanır ve crash sonrası genellikle kendini otomatik olarak kurtarır. MySQL başladığında InnoDB recovery işlemini otomatik yapar.
Ama bazen bu yeterli olmaz. Ciddi bir disk hasarı, bozuk system tablespace veya yarım kalmış büyük bir transaction varsa MySQL başlamayı reddedebilir. İşte bu noktada innodb_force_recovery devreye girer.
innodb_force_recovery Seviyeleri
Bu parametreyi my.cnf dosyasına ekliyorsun ve MySQL’i yeniden başlatıyorsun. Seviyeleri ve ne anlama geldikleri:
innodb_force_recovery = 1 (SRV_FORCE_IGNORE_CORRUPT): Bozuk page’leri atlar, devam etmeye çalışır. En hafif müdahale. “Bu page okunamıyor, geç git” modudur.
innodb_force_recovery = 2 (SRV_FORCE_NO_BACKGROUND): Arka plan thread’lerini çalıştırmaz (master thread, purge thread gibi). Crash recovery sırasında crash oluyorsa dene.
innodb_force_recovery = 3 (SRV_FORCE_NO_TRX_UNDO): Transaction rollback’leri yapmaz. Açık kalmış transaction’lar geri alınmaz. Recovery işlemi sonrası da rollback çalışmaz.
innodb_force_recovery = 4 (SRV_FORCE_NO_IBUF_MERGE): Insert buffer merge işlemlerini yapmaz. Insert buffer bozuksa bu seviye işe yarar. Bu seviye ve üzeri veri kaybına yol açabilir.
innodb_force_recovery = 5 (SRV_FORCE_NO_UNDO_LOG_SCAN): Undo log’ları okumaz. Commit edilmemiş transaction’ları committed gibi gösterir. Ciddi veri tutarsızlığı riski var.
innodb_force_recovery = 6 (SRV_FORCE_NO_LOG_REDO): Redo log işlemlerini tamamen atlar. En son çare. Bu seviyede veri kaybı neredeyse kesindir, ama en azından bir şeyler kurtarabilirsin.
Altın kural: Her zaman en düşük seviyeyle başla. 1’den başla, olmadı 2’ye geç, olmadı 3’e geç. Gereksiz yere yüksek seviyeye atlamak veri kaybını artırır.
innodb_force_recovery Nasıl Kullanılır?
# my.cnf veya my.ini dosyasını düzenle
nano /etc/mysql/my.cnf
# [mysqld] bölümüne ekle
[mysqld]
innodb_force_recovery = 1
MySQL’i yeniden başlat ve logları izle:
systemctl restart mysql
tail -f /var/log/mysql/error.log
MySQL başarıyla başladıysa ve logda hata yoksa, hemen veriyi dışarı çıkar:
# Kritik verileri dump al
mysqldump -u root -p --single-transaction mydb orders > /backup/orders_rescue.sql
# Tüm veritabanını dump al
mysqldump -u root -p --all-databases --single-transaction > /backup/full_rescue.sql
Gerçek Dünya Senaryosu: MySQL Hiç Başlamıyor
Klasik senaryo: Sunucu güç kesintisinden sonra MySQL bir türlü başlamıyor. Error log’da şunlar var:
[ERROR] InnoDB: Page [page id: space=0, page number=7] log sequence number 2345678
is in the future! Current system log sequence number 1234567.
[ERROR] InnoDB: Your database may be corrupt or you may have copied the InnoDB
tablespace but not the InnoDB log files.
Adım adım çözüm:
# Önce backup al - ne olursa olsun
cp -a /var/lib/mysql /backup/mysql_emergency_$(date +%Y%m%d_%H%M%S)
# my.cnf'e level 1 ekle
echo "innodb_force_recovery = 1" >> /etc/mysql/mysql.conf.d/mysqld.cnf
# MySQL'i başlatmayı dene
systemctl start mysql
sleep 5
systemctl status mysql
# Başarılı olduysa dump al ve devam et
# Başarısız olduysa level'i artır
Level 1 işe yaramadı, level 2’ye geç:
# my.cnf'i düzenle
sed -i 's/innodb_force_recovery = 1/innodb_force_recovery = 2/' /etc/mysql/mysql.conf.d/mysqld.cnf
systemctl start mysql
systemctl status mysql
Level 3’e geçmeden önce ciddi bir uyarı: artık bazı veriler tutarsız olabilir. Dump aldıktan sonra veritabanını tamamen temizleyip yeniden oluşturacaksın.
# Level 3 ile MySQL ayağa kalktıysa
sed -i 's/innodb_force_recovery = 2/innodb_force_recovery = 3/' /etc/mysql/mysql.conf.d/mysqld.cnf
systemctl start mysql
# Başarıyla başladıysa HEMEN dump al
mysqldump -u root -p mydb > /backup/mydb_force_recovery.sql 2>&1
# Dump başarılıysa InnoDB tablespace'i temizle
systemctl stop mysql
# innodb_force_recovery satırını kaldır
sed -i '/innodb_force_recovery/d' /etc/mysql/mysql.conf.d/mysqld.cnf
# InnoDB dosyalarını temizle
rm /var/lib/mysql/ibdata1
rm /var/lib/mysql/ib_logfile*
# MySQL'i yeniden başlat (temiz başlangıç)
systemctl start mysql
# Dump'ı geri yükle
mysql -u root -p mydb < /backup/mydb_force_recovery.sql
Level 4 ve Üzeri: Son Çare Prosedürü
Eğer 1-3 seviyesi işe yaramadıysa ve veritabanı hala başlamıyorsa, artık veri kurtarma modundasın, tam recovery değil.
# my.cnf
[mysqld]
innodb_force_recovery = 4
innodb_purge_threads = 0
Level 4 ve üzerinde yalnızca okuma işlemi yapabilirsin. Yazma denemesi MySQL’i çökertebilir. Tek amacın veriyi okuyup başka bir yere aktarmak.
# Sadece okuma amaçlı sorgular çalıştır
mysql -u root -p
# Her tabloyu ayrı ayrı dışarı çıkar
mysql> SELECT * INTO OUTFILE '/tmp/orders_rescue.csv' FROM mydb.orders;
mysql> SELECT * INTO OUTFILE '/tmp/users_rescue.csv' FROM mydb.users;
Önleyici Tedbirler
Bozulma sonrası onarım stresli ve zaman alıcı. Bunun yerine şu önlemleri almak çok daha mantıklı:
# InnoDB için düzenli kontrol
mysql -u root -p -e "SELECT table_schema, table_name, engine
FROM information_schema.tables
WHERE engine = 'MyISAM' AND table_schema NOT IN ('information_schema','mysql','performance_schema');"
Hala MyISAM kullanan tablolar varsa InnoDB’ye geçir:
# MyISAM'dan InnoDB'ye dönüştür
mysql -u root -p -e "ALTER TABLE mydb.orders ENGINE=InnoDB;"
Düzenli backup için cron job:
# /etc/cron.d/mysql-backup dosyası oluştur
cat > /etc/cron.d/mysql-backup << 'EOF'
0 2 * * * root mysqldump -u backup_user -pPASSWORD --all-databases --single-transaction --quick | gzip > /backup/mysql_$(date +%Y%m%d).sql.gz && find /backup -name "mysql_*.sql.gz" -mtime +7 -delete
EOF
my.cnf için önemli InnoDB ayarları:
[mysqld]
# Crash sonrası otomatik recovery
innodb_force_recovery = 0
# Redo log boyutunu artır (büyük transaction'lar için)
innodb_log_file_size = 256M
innodb_log_buffer_size = 64M
# Her transaction'da fsync zorla (yavaşlatır ama güvenli)
innodb_flush_log_at_trx_commit = 1
# Double write buffer (disk corruption karşısında koruma)
innodb_doublewrite = ON
# Checksums aktif olsun
innodb_checksum_algorithm = crc32
Sık Karşılaşılan Hatalar ve Çözümleri
“Table is full” hatası: Bu bozulma değil, innodb_data_file_path ile tablespace boyutunu artır.
“Got error 28 from storage engine”: Disk dolmuş. Önce disk temizle, sonra onar.
myisamchk çalışmıyor, “table is in use” diyor: MySQL hala çalışıyor. systemctl stop mysql yapıp tekrar dene.
innodb_force_recovery = 6 ile bile başlamıyor: Artık fiziksel disk kurtarma araçlarının (testdisk, photorec) zamanı gelmiş olabilir. Donanım arızası ihtimalini değerlendir.
# Hızlı disk sağlığı kontrolü
smartctl -a /dev/sda | grep -E "Reallocated|Uncorrectable|Pending"
# Dosya sistemi hatası var mı
dmesg | grep -i "error|corrupt|I/O error" | tail -20
Sonuç
MySQL tablo bozulması her sysadmin’in en az bir kez yaşadığı ve genellikle gece yarısı yaşadığı bir kriz. Özetle şunu unutma: MyISAM için myisamchk, InnoDB için innodb_force_recovery. Her iki durumda da ilk adım her zaman backup almak, ikinci adım mümkün olan en düşük müdahale seviyesiyle başlamak.
innodb_force_recovery‘de seviyeyi gereksiz yükseltmek veri kaybını artırır. Level 1’den başla, her adımda durumu değerlendir. MySQL başarıyla ayağa kalkar kalkmaz dump almak için tek dakika bile bekleme, hemen mysqldump‘ı çalıştır.
Uzun vadede ise çözüm basit: MyISAM’ı bırak, InnoDB kullan, düzenli backup al, innodb_flush_log_at_trx_commit = 1 ayarını koy. Bu dört şeyi yapan biri, gece 3’te telefon açmak zorunda kalmaz.
