Veritabanı Yönetimi

    Veritabanı Felaket Kurtarma Planı: RTO ve RPO

    RTO ve RPO hedeflerini belirleyip test edilmiş bir veritabanı kurtarma planı kurmanın yolu.

    10 dk okuma Güncellendi: 25 Ağustos 2026

    Bir cuma akşamı yanlış terminalde DROP TABLE siparisler; çalıştırdığınızda ya da disk denetleyicisi sessizce bozuk blok yazmaya başladığında elinizde kalan tek şey yedeklerinizdir. Ama "yedeğim var" cümlesi tek başına hiçbir şey ifade etmez. Asıl iki soru şudur: o yedek kaç saatlik veriyi kaybettiriyor ve geri yüklemek kaç saat sürüyor? Veritabanı felaket kurtarma planı, bu iki soruya önceden yazılı olarak verilmiş cevaptır; bu cevapların teknik adları da RPO ve RTO'dur.

    Bu rehberde RTO ile RPO'nun ne ölçtüğünü, kendi sisteminiz için gerçekçi hedefleri nasıl belirleyeceğinizi, tek bir mysqldump dosyasının neden yeterli olmadığını, binlog ile zaman noktasına geri dönüşü (PITR), kurtarma tatbikatının nasıl yapılacağını ve felaket anında hangi sırayla hareket edileceğini anlatacağım. Sonunda da yıllardır aynı kurumlarda tekrar tekrar gördüğüm tuzakları tek tek sıralayacağım — asıl değer orada.

    RTO ve RPO Tam Olarak Neyi Ölçer#

    RPO (Recovery Point Objective), felaket anında kabul edebileceğiniz veri kaybı penceresidir. Yani "en fazla kaç dakikalık işlemi kaybedebilirim?" sorusunun cevabıdır. Günde bir kez gece 03:00'te yedek alıyorsanız ve sistem saat 17:00'de çöktüyse, RPO'nuz 14 saattir: o 14 saatte girilen tüm siparişler, kayıtlar ve ödemeler gitmiştir. RPO'yu belirleyen şey yedek sıklığıdır, hızı değil.

    RTO (Recovery Time Objective) ise felaketten sonra sistemin tekrar ayağa kalkması için kabul edilebilir süredir. Yedeğiniz 5 dakika öncesine ait olabilir ama 400 GB'lık bir dump dosyasını tek çekirdekli bir makineye geri yüklemek 9 saat sürüyorsa RTO'nuz 9 saattir. RTO'yu belirleyen şey ise yedek biçimi, donanım ve prosedürün ne kadar prova edilmiş olduğudur. İki metriği birbirine karıştırmak çok yaygındır; şu tablo ayrımı netleştirir:

    MetrikSorusuBelirleyen faktörİyileştirme yolu
    RPONe kadar veri kaybederim?Yedek/log sıklığıBinlog arşivi, replika, daha sık snapshot
    RTONe kadar sürede açılırım?Geri yükleme hızı ve prosedürFiziksel yedek, hazır bekleyen replika, yazılı runbook

    Sıfır RPO ve sıfır RTO teoride mümkündür ama maliyeti üstel biçimde artar; senkron replikasyon, çift veri merkezi ve otomatik failover gerektirir. Küçük bir e-ticaret sitesi için "RPO 15 dakika, RTO 2 saat" fazlasıyla makul bir hedeftir ve tek bir VDS üzerinde bile kurulabilir.

    Kendi Hedeflerinizi Belirlemek#

    Hedefleri teknik ekip değil, iş tarafı belirlemelidir; teknik ekip yalnızca maliyetini söyler. Pratikte izlediğim yöntem şu üç soruyu iş sahibine sormaktır: Bir saatlik kesintinin size parasal maliyeti nedir? Kaybolan son bir saatlik siparişi telefonla geri toplayabilir misiniz? Sistem 6 saat kapalı kalırsa müşteri kaybeder misiniz? Alacağınız cevaplar hedefi kendiliğinden ortaya koyar.

    Aşağıdaki tablo, farklı iş tiplerinde sahada gördüğüm makul başlangıç hedefleridir. Bunları kopyalayıp geçmeyin; kendi rakamlarınızla doğrulayın:

    Sistem tipiMakul RPOMakul RTOGerekli yapı
    Kurumsal tanıtım sitesi24 saat4-8 saatGünlük mantıksal dump + offsite kopya
    Blog / içerik sitesi6 saat2-4 saatGünlük dump + 6 saatlik artımlı
    E-ticaret15 dakika1-2 saatBinlog arşivi + fiziksel yedek
    SaaS / ödeme akışı1 dakika15 dakikaReplika + otomatik failover

    Hedefi yazıya dökün ve bir yere asın. Yazılı olmayan hedef, felaket anında "bir de şunu deneyelim" turlarına dönüşür ve RTO'yu üçe katlayan şey tam olarak bu doğaçlamadır.

    Yedek Katmanları: Tek Bir Dump Yeterli Değil#

    Sağlam bir plan tek bir yedek türüne dayanmaz, üç katmandan oluşur. Birinci katman mantıksal yedektir: mysqldump ya da pg_dump ile alınan SQL metni. Taşınabilir, sürüm bağımsız, tek tablo geri yüklemeye uygundur; ama büyük veritabanlarında hem alınması hem geri yüklenmesi yavaştır. Ayrıntılar için mysqldump ile veritabanı yedekleme yazısına bakabilirsiniz.

    # Tutarlı, tek işlemli mantıksal yedek (InnoDB için kilitsiz)
    mysqldump --single-transaction --quick --routines --triggers --events \
      --source-data=2 --databases uygulama_db \
      | gzip -c > /yedek/uygulama_db-$(date +%F-%H%M).sql.gz
    
    # PostgreSQL karşılığı, sıkıştırılmış özel biçimde
    pg_dump -Fc -Z6 -d uygulama_db -f /yedek/uygulama_db-$(date +%F).dump
    

    İkinci katman fiziksel yedektir: veri dosyalarının blok düzeyinde kopyası. MySQL/MariaDB tarafında mariabackup ya da Percona XtraBackup, PostgreSQL tarafında pg_basebackup bu işi yapar. 200 GB'lık bir veritabanında mantıksal dump'ın geri yüklenmesi saatler alırken, fiziksel yedek dosyaları yerine kopyalayıp servisi başlatmak dakikalar sürer — yani RTO'yu asıl düşüren katman budur.

    # MariaDB fiziksel yedek
    mariabackup --backup --target-dir=/yedek/fiziksel/$(date +%F) \
      --user=yedekci --password='parola'
    
    # Geri yüklemeden önce log'ların uygulanması (prepare) şart
    mariabackup --prepare --target-dir=/yedek/fiziksel/2026-08-25
    

    Üçüncü katman kopyanın kopyasıdır: 3-2-1 kuralı. En az 3 kopya, 2 farklı ortam, 1 tanesi tamamen başka bir fiziksel konumda. Aynı sunucunun /yedek dizininde duran dosya, sunucu diski öldüğünde yedek değildir. Bu katmanı otomatikleştirmek için yedekleme hizmetimiz gibi sunucu dışına yazan bir hedef kullanmak, sahada gördüğüm en sık atlanan ama en ucuz kazanç.

    Binlog ile Zaman Noktasına Geri Dönüş#

    RPO'yu dakikalar seviyesine indirmenin yolu, iki yedek arasındaki değişiklikleri kaydeden işlem günlüklerini saklamaktır. MySQL/MariaDB'de bu binary log (binlog), PostgreSQL'de WAL arşividir. Gece 03:00'teki tam yedeği geri yükleyip üzerine 03:00 ile hatanın olduğu 16:42 arasındaki binlog'ları uygularsanız, veriyi 16:42'ye kadar kurtarmış olursunuz. Bunun adı Point-In-Time Recovery'dir.

    Binlog'u açmak için my.cnf içinde şu ayarlar yeterlidir:

    [mysqld]
    log_bin = /var/log/mysql/binlog
    binlog_format = ROW
    binlog_expire_logs_seconds = 604800
    sync_binlog = 1
    server_id = 1
    

    binlog_format = ROW seçimi önemlidir; STATEMENT biçimi deterministik olmayan sorgularda geri yüklemeyi bozar. sync_binlog = 1 her işlemi diske yazdırır, biraz yavaşlatır ama çöküşte log kaybını engeller. Yanlışlıkla silinen bir tablodan sonra geri dönüş şu sırayla yapılır:

    1. Uygulamayı ve yazma trafiğini durdurun; hatalı sunucuya bir daha yazmayın.
    2. En son tam yedeği ayrı bir kurtarma sunucusuna geri yükleyin.
    3. Yedeğin bittiği binlog konumunu (--source-data=2 çıktısındaki CHANGE MASTER TO satırı) not edin.
    4. O konumdan hatanın gerçekleştiği ana kadar binlog'ları uygulayın.
    5. Kurtarılan veriyi doğrulayın, sonra üretime alın.
    # Hatalı DROP'un zamanı 16:42:10 ise, o ana kadar uygula
    mysqlbinlog --start-position=194 --stop-datetime="2026-08-25 16:42:00" \
      /var/log/mysql/binlog.000117 | mysql -u root -p uygulama_db
    

    Binlog'ların diski doldurması ayrı bir dert kaynağıdır; saklama süresini ayarlamayı unutursanız MySQL binlog diski doldurdu yazısındaki senaryoyla karşılaşırsınız.

    Kurtarma Tatbikatı: Test Edilmemiş Yedek Yedek Değildir#

    Sahada gördüğüm en acı manzara, üç yıldır düzenli çalışan bir yedek görevinin aslında boş dosyalar ürettiğinin felaket gününde anlaşılmasıdır. Cron çıktısını kimse okumamış, dosya boyutu kimsenin dikkatini çekmemiştir. Bu yüzden plan, "yedek alınıyor mu?" sorusuyla değil "yedekten dönebiliyor muyuz?" sorusuyla denetlenmelidir.

    Tatbikatı üç ayda bir, ayrı bir makinede yapın ve süreyi kronometreyle ölçün. Ölçtüğünüz süre sizin gerçek RTO'nuzdur; hedefinizden büyükse plan hedefi tutmuyordur. Otomatik bir doğrulama betiği yazmak da mümkündür:

    #!/bin/bash
    # En son yedeği geçici bir veritabanına yükle ve satır sayısını doğrula
    set -euo pipefail
    SON=$(ls -1t /yedek/*.sql.gz | head -1)
    mysql -e "DROP DATABASE IF EXISTS dogrulama; CREATE DATABASE dogrulama;"
    gunzip -c "$SON" | mysql dogrulama
    SATIR=$(mysql -N -B -e "SELECT COUNT(*) FROM dogrulama.siparisler;")
    if [ "$SATIR" -lt 1000 ]; then
      echo "UYARI: $SON içinde beklenenden az satır var ($SATIR)" >&2
      exit 1
    fi
    echo "Doğrulama başarılı: $SON / $SATIR satır"
    

    Bu betiği haftada bir çalıştırıp çıktısını izleme sisteminize bağlayın. Hangi metrikleri izlemeniz gerektiğini veritabanı izleme metrikleri yazısında ayrıntılı anlattım; yedek yaşı ve replika gecikmesi o listenin en tepesinde olmalı.

    Felaket Anında Uygulanacak Sıra#

    Panik anında düşünmek zorunda kalmamak için yazılı bir runbook şarttır. Sırası şudur ve bu sıra tartışmaya açık değildir:

    1. Durdur. Yazma trafiğini kesin. Bozuk veriye yazmaya devam etmek, kurtarılabilir durumu kurtarılamaz hale getirir.
    2. Teşhis et. Sorun donanım mı, insan hatası mı, yazılım hatası mı? Yanlış teşhis, yanlış kurtarma yöntemi demektir.
    3. Kanıtı koru. Bozuk veri dizininin bir kopyasını alın. Kurtarma denemeleri sırasında elinizdeki tek delili silmeyin.
    4. Ayrı makinede kurtar. Asla doğrudan üretim sunucusunun üstüne geri yükleme yapmayın.
    5. Doğrula. Satır sayıları, son sipariş numarası, kritik tabloların bütünlüğü.
    6. Devret. Uygulamayı yeni veritabanına yönlendirin, trafiği kademeli açın.
    7. Yaz. Olay sonrası raporu aynı gün yazın; bir hafta sonra ayrıntıları hatırlamazsınız.

    Bu adımların her birinin yanına sorumlu kişinin adını ve telefonunu ekleyin. Gece 03:00'te "kimin yetkisi var?" diye aramakla geçen 40 dakika, doğrudan RTO'nuza eklenir.

    Sık Yapılan Hatalar#

    Yedeği aynı sunucuda tutmak. En yaygın hata. Disk arızası, fidye yazılımı ya da yanlış rm -rf aynı anda hem veriyi hem yedeği götürür. Yedek en az bir kopya olarak farklı bir makinede, tercihen farklı bir konumda durmalıdır.

    Sadece replikaya güvenmek. Replikasyon yüksek erişilebilirlik sağlar, yedek sağlamaz. DROP TABLE komutu saniyeler içinde replikaya da gider. MySQL replikasyon kurulumu yapmış olmak, yedek almadığınız gerçeğini değiştirmez.

    Yedek kullanıcısına gereksiz yetki vermek. Yedek alan hesap root olmak zorunda değildir; SELECT, RELOAD, PROCESS, LOCK TABLES ve REPLICATION CLIENT yetkileri yeterlidir. Parolayı komut satırına yazmak yerine ~/.my.cnf dosyasına 0600 izniyle koyun; komut satırındaki parola ps çıktısında görünür.

    Sıkıştırılmış dosyanın bütünlüğünü hiç kontrol etmemek. gzip -t dosya.sql.gz bir saniye sürer ve yarım kalmış bir yedeği anında ele verir. Yedek görevinin sonuna bu kontrolü ekleyin.

    Şifreleme anahtarını yedekle aynı yere koymak. Şifreli yedek iyi bir fikirdir, ama anahtar da aynı dizindeyse şifreleme sadece sizi yavaşlatır. Anahtarı ayrı bir kasada saklayın ve anahtarın da bir yedeği olsun — anahtarını kaybettiğiniz yedek, silinmiş yedektir.

    Sıkça Sorulan Sorular#

    RTO ve RPO arasındaki fark tam olarak nedir#

    RPO veri kaybı penceresidir, RTO ise kesinti süresidir. RPO "en son hangi ana kadar geri dönebilirim?" sorusuna, RTO ise "sistemi ne kadar sürede tekrar açabilirim?" sorusuna cevap verir. İkisi birbirinden bağımsız iyileştirilir: RPO'yu daha sık yedek ve log arşivi düşürür, RTO'yu ise fiziksel yedek ve prova edilmiş prosedür kısaltır.

    Veritabanı yedeğini ne sıklıkla almalıyım#

    Sıklığı doğrudan kabul ettiğiniz RPO belirler. 24 saatlik veri kaybını göze alabiliyorsanız günlük tam yedek yeterlidir; 15 dakikalık kayıp sınırınız varsa günlük tam yedeğin yanına sürekli binlog arşivi eklemelisiniz. Pratikte çoğu e-ticaret sitesi için gece tam yedek artı sürekli binlog arşivi doğru dengeyi verir.

    Replikasyon yedek yerine geçer mi#

    Hayır. Replikasyon sunucu arızasına karşı korur, insan hatasına ve mantıksal bozulmaya karşı korumaz. Yanlışlıkla silinen bir tablo saniyeler içinde replikaya da yansır ve orada da yoktur. Replika, RTO'yu düşüren bir yüksek erişilebilirlik aracıdır; yedek ise zamanda geriye gitme aracıdır. İkisi birbirinin yerine değil, yan yana kullanılır.

    Yedeğimin sağlam olduğunu nasıl kontrol ederim#

    Tek geçerli yöntem geri yüklemektir. Sıkıştırılmış dosya için gzip -t ile bütünlük kontrolü ilk adımdır ama yeterli değildir; ayrı bir makineye geri yükleyip kritik tabloların satır sayılarını ve son kaydın tarihini doğrulamanız gerekir. Bunu otomatik bir betikle haftalık çalıştırmak, felaket gününde sürpriz yaşamamanın en ucuz yoludur.

    Point-in-time recovery için hangi ayarlar gerekir#

    MySQL ve MariaDB'de log_bin açık, binlog_format = ROW ve makul bir binlog_expire_logs_seconds değeri gerekir; ayrıca binlog dosyalarının da yedeklenmesi şarttır çünkü sunucu ölürse loglar da ölür. PostgreSQL'de karşılığı wal_level = replica ve bir archive_command ile WAL arşivlemektir. İki tarafta da tam yedeğin hangi log konumunda bittiğini kaydetmek zorunludur.

    Küçük bir site için felaket kurtarma planı fazla mı kaçar#

    Hayır, sadece ölçeği küçülür. Küçük bir site için plan tek sayfa olabilir: günlük otomatik dump, sunucu dışına kopya, ayda bir geri yükleme denemesi ve kimin hangi paneli açacağını yazan üç satır. Maliyeti neredeyse sıfırdır ama tek bir olayda işinizi kurtarır. Plansız kalmanın maliyeti ise her zaman planın maliyetinden yüksektir.

    Kapanış#

    Felaket kurtarma planı, karmaşık bir mimari değil dört sağlam alışkanlıktır: hedefinizi (RPO/RTO) yazılı olarak belirlemek, yedeği en az bir kopya olarak sunucu dışında tutmak, iki yedek arasını binlog ya da WAL arşiviyle kapatmak ve yedekten dönüşü düzenli olarak prova edip süreyi ölçmek. Bu dördünü yapan bir ekip, gerçekten kötü bir günde saatler değil dakikalar konuşur.

    Bu işi tek başınıza kurmak istemiyorsanız Clou.TR tarafında hazır çözümler var: sunucu dışına otomatik kopya alan yedekleme hizmetimiz, kendi kurtarma sunucunuzu ayağa kaldırmak için tam root erişimli VDS ve bulut sunucu paketlerimiz, tatbikat ve izleme dahil tüm operasyonu üstlenmemizi isterseniz de sunucu yönetimi hizmetimiz. Hangi yolu seçerseniz seçin, planınızı bir kez de gerçek veriyle deneyin.

    MySQLYedeklemeFelaket Kurtarma

    Uygulamaya geçmeye hazır mısınız?

    NVMe SSD, ücretsiz SSL ve %99.9 uptime garantisiyle Clou.TR hosting ve sunucu çözümleriyle projenizi hayata geçirin.