Veritabanı Yönetimi

    Binlog ile Point-in-Time Recovery

    Yedek ve binlog birleştirilerek veritabanını hatalı komuttan bir saniye öncesine döndürme.

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

    Saat 14.32'de biri WHERE yazmayı unutup bir UPDATE çalıştırdı ve on binlerce sipariş satırının durumu bozuldu. Elindeki en son yedek gece 03.00'te alınmış. Yedeği geri yüklersen 14.32'ye kadar girilen tüm gerçek veriyi kaybedersin; yüklemezsen bozuk veriyle yaşarsın. Binlog ile point-in-time recovery bu ikilemi ortadan kaldırır: gece yarısının yedeğini geri yükler, ardından binary log dosyalarındaki değişiklikleri 14.31.59'a kadar tekrar uygular ve veritabanını felaketten bir saniye öncesine döndürürsün.

    Bu rehberde binary log'un ne tuttuğunu, PITR'in çalışabilmesi için sunucunun nasıl yapılandırılması gerektiğini, yedek alırken binlog konumunun neden kaydedilmesi gerektiğini, mysqlbinlog ile hatalı anın tam olarak nasıl bulunacağını ve kurtarmanın adım adım nasıl yapılacağını anlatacağım. GTID kullanan kurulumlar için ayrı bir bölüm ve kurtarma sırasında en çok yapılan hatalara ayrı bir başlık ayırdım — çünkü PITR'de asıl risk kurtarmanın kendisidir, hazırlığı değil.

    Point-in-Time Recovery Neyi Kurtarır, Neyi Kurtarmaz#

    Binary log, MySQL'in veriyi değiştiren her işlemi sırayla kaydettiği dosya serisidir. INSERT, UPDATE, DELETE, ALTER, CREATE gibi tüm değişiklikler buraya yazılır; SELECT gibi okuma sorguları yazılmaz. Replikasyonun temelinde de bu dosyalar vardır: replika, master'ın binlog'unu okuyup aynı değişiklikleri kendi üzerinde uygular. PITR aynı mekanizmayı zaman içinde geriye doğru bir kurtarma aracına dönüştürür.

    Buradaki kritik nokta şudur: binlog tek başına yeterli değildir. Binlog yalnızca değişikliklerin kaydıdır, verinin kendisini içermez. Kurtarma her zaman iki parçadan oluşur — bir tam yedek ve o yedeğin alındığı andan itibaren biriken binlog dosyaları. Yedek yoksa binlog bir işe yaramaz; binlog yoksa yedeğin alındığı ana geri dönmek zorunda kalırsın. Bu yüzden PITR bir yedekleme stratejisinin yerine geçmez, onu tamamlar. Aşağıdaki tablo bu ilişkiyi netleştiriyor:

    ElindekiGeri dönebileceğin noktaKayıp
    Sadece gecelik yedekGece 03.0011,5 saatlik veri
    Yedek + binlog14.31.59Saniyeler
    Sadece binlogHiçbir yereTümü
    Yedek + binlog + replika14.31.59Saniyeler, üstelik daha hızlı

    Bir diğer sınır da şudur: DROP DATABASE ya da tablo dosyalarının diskten silinmesi gibi durumlarda binlog yine işe yarar, ama disk arızası nedeniyle binlog dosyaları da kaybolduysa hiçbir şey yapılamaz. Bu yüzden binlog dosyalarını veri dizininden farklı bir diske yazmak ve düzenli olarak sunucu dışına kopyalamak, PITR stratejisinin görünmeyen ama en önemli parçasıdır.

    Binary Log'u Doğru Yapılandırmak#

    PITR'in mümkün olması için binlog'un açık ve doğru biçimde ayarlanmış olması gerekir. MySQL 8 ve modern MariaDB sürümlerinde binlog varsayılan olarak açıktır ama saklama süresi ve format ayarlarını mutlaka gözden geçirmelisin.

    # /etc/mysql/mysql.conf.d/mysqld.cnf
    [mysqld]
    server-id               = 1
    log_bin                 = /var/log/mysql-binlog/mysql-bin
    binlog_format           = ROW
    binlog_row_image        = FULL
    max_binlog_size         = 256M
    binlog_expire_logs_seconds = 1209600   # 14 gün
    sync_binlog             = 1
    

    Bu satırların her biri bir sebeple orada. binlog_format=ROW, değişikliği "hangi sorgu çalıştı" olarak değil "hangi satır ne oldu" olarak kaydeder; bu, NOW() ya da RAND() gibi belirsiz fonksiyonlar içeren sorguların yeniden uygulandığında farklı sonuç üretmesini engeller. binlog_row_image=FULL satırın tüm sütunlarını kaydeder ve kurtarma sırasında ne olduğunu okuyabilmeni sağlar. sync_binlog=1 her işlemden sonra binlog'u diske senkronize eder; bu ayar biraz performans maliyeti getirir ama ani bir çökmede binlog'un son saniyelerini kaybetmeni önler, ki PITR'in tüm amacı budur.

    binlog_expire_logs_seconds değeri, yedekleme sıklığından daha uzun olmalıdır. Haftalık tam yedek alıyorsan binlog'u yedi günden az tutmak, en kötü senaryoda kurtarma zincirini kırar. Ayrıca binlog'u ayrı bir dizine yazmak iyi bir alışkanlıktır; veri diski dolduğunda binlog yazamayan MySQL yazma işlemlerini reddeder. Bu sorunla karşılaştıysan MySQL binlog diski doldurdu yazısı temizleme adımlarını anlatıyor. Ayarları uyguladıktan sonra doğrula:

    mysql -e "SHOW VARIABLES LIKE 'log_bin'; SHOW VARIABLES LIKE 'binlog_format';"
    mysql -e "SHOW BINARY LOGS;"
    

    Yedek Alırken Binlog Konumunu Kaydetmek#

    PITR'in en sık atlanan adımı budur ve atlandığında kurtarma neredeyse imkânsız hâle gelir. Binlog'u yedeğin üzerine uygularken tam olarak nereden başlaman gerektiğini bilmelisin. Bu bilgi, yedeğin alındığı andaki binlog dosyası adı ve konum numarasıdır.

    mysqldump kullanıyorsan bu bilgiyi dump dosyasının içine yazdırabilirsin:

    mysqldump --single-transaction --source-data=2 --all-databases \
      --routines --events --triggers \
      | gzip > /yedek/tam-$(date +%F).sql.gz
    

    --source-data=2 seçeneği (eski sürümlerde --master-data=2) dump dosyasının başına yorum satırı olarak konumu yazar. Yedeği geri yüklemeden önce bu satırı okursun:

    zcat /yedek/tam-2026-08-25.sql.gz | head -30 | grep CHANGE
    # -- CHANGE REPLICATION SOURCE TO SOURCE_LOG_FILE='mysql-bin.000047', SOURCE_LOG_POS=8842176;
    

    XtraBackup kullanıyorsan aynı bilgi yedek dizinindeki xtrabackup_binlog_info dosyasında durur:

    cat /yedek/tam-2026-08-25/xtrabackup_binlog_info
    # mysql-bin.000047    8842176
    

    Bu iki değeri yedek dosyasının yanında saklamak, gece yarısı yapacağın işi dakikalar seviyesine indirir. Fiziksel yedek almanın ayrıntıları için Percona XtraBackup ile sıcak yedekleme, mantıksal yedek için mysqldump ile veritabanı yedekleme yazılarına bakabilirsin. Yedekleri sunucu dışında da tutmak istersen yedekleme hizmetimiz bu kopyalamayı zamanlanmış olarak yapar.

    Binlog İçeriğini Okumak ve Hatalı Anı Bulmak#

    Kurtarmaya başlamadan önce hatanın tam olarak hangi binlog konumunda gerçekleştiğini bulmalısın. mysqlbinlog aracı ikili dosyayı okunabilir SQL'e çevirir; ROW formatındaki satır olaylarını görebilmek için iki parametre gerekir.

    mysqlbinlog --base64-output=DECODE-ROWS --verbose \
      /var/log/mysql-binlog/mysql-bin.000049 | less
    

    Çıktıda her olayın başında at ile başlayan bir konum numarası ve zaman damgası bulunur:

    # at 41982
    #260825 14:32:07 server id 1  end_log_pos 42150 CRC32 0x9a3f1c2e  Update_rows: table id 218 flags: STMT_END_F
    ### UPDATE `magaza`.`siparis`
    ### WHERE
    ###   @1=10482
    ###   @5='hazirlaniyor'
    ### SET
    ###   @1=10482
    ###   @5='iptal'
    

    Aradığın anı daraltmak için zaman filtresi kullan. Hangi dosyada olduğunu bilmiyorsan SHOW BINARY LOGS çıktısındaki dosyaları sırayla tara:

    # 14:25 ile 14:40 arasını incele
    mysqlbinlog --base64-output=DECODE-ROWS --verbose \
      --start-datetime="2026-08-25 14:25:00" \
      --stop-datetime="2026-08-25 14:40:00" \
      /var/log/mysql-binlog/mysql-bin.000049 | grep -n -A5 "Update_rows"
    

    Hatalı işlemi bulduğunda, o işlemin başladığı konumu not et. Örnekte hatalı UPDATE 41982 konumunda başlıyor; kurtarmayı bu konuma kadar uygulayıp orada durduracaksın. Zaman damgasına göre durmak da mümkündür ama konum numarası çok daha kesindir: aynı saniye içinde birden fazla işlem olabilir ve zaman bazlı durdurma bunları ayıramaz. Mümkün olduğunda daima konum kullan.

    Kurtarmayı Adım Adım Uygulamak#

    Kurtarmayı asla doğrudan üretim sunucusunda yapma. Doğru yöntem, ayrı bir sunucuda ya da farklı bir portta ikinci bir MySQL örneği ayağa kaldırıp kurtarmayı orada yapmak, sonucu doğrulamak ve yalnızca ihtiyacın olan tabloları üretime taşımaktır. Aşağıdaki akış bu mantıkla ilerliyor.

    1. Binlog dosyalarını güvene al. Kurtarma sırasında sunucu binlog yazmaya devam eder ve eski dosyalar süre dolduğu için silinebilir. İlk iş kopyalamaktır:
    sudo cp /var/log/mysql-binlog/mysql-bin.0000[4-9]* /kurtarma/binlogs/
    
    1. Tam yedeği kurtarma örneğine geri yükle.
    zcat /yedek/tam-2026-08-25.sql.gz | mysql -h 127.0.0.1 -P 3307 -u root -p
    
    1. Yedek konumundan hatanın konumuna kadar olan binlog'u SQL'e çevir. Yedek mysql-bin.000047 dosyasının 8842176 konumunda alındıysa ve hata mysql-bin.000049 dosyasının 41982 konumundaysa, aradaki tüm dosyaları tek bir komutta ve sırayla vermelisin:
    mysqlbinlog \
      --start-position=8842176 \
      --stop-position=41982 \
      /kurtarma/binlogs/mysql-bin.000047 \
      /kurtarma/binlogs/mysql-bin.000048 \
      /kurtarma/binlogs/mysql-bin.000049 \
      > /kurtarma/replay.sql
    

    Dosyaları tek komutta vermek önemlidir; ayrı ayrı çalıştırırsan her dosya kendi başına yorumlanır ve açık kalan işlemler bölünür. --start-position yalnızca ilk dosyaya, --stop-position yalnızca son dosyaya uygulanır.

    1. Üretilen SQL'i gözden geçir. Bu adım isteğe bağlı görünür ama en değerli olanıdır; dosyanın sonunda hatalı işlemin gerçekten bulunmadığını doğrula:
    tail -40 /kurtarma/replay.sql
    grep -c "UPDATE" /kurtarma/replay.sql
    
    1. Kurtarma örneğine uygula. Uygularken bu değişikliklerin tekrar binlog'a yazılmasını istemezsin:
    mysql -h 127.0.0.1 -P 3307 -u root -p -e "SET sql_log_bin=0; SOURCE /kurtarma/replay.sql;"
    
    1. Sonucu doğrula ve gerekli veriyi üretime taşı. Genellikle tek bir tablonun düzeltilmesi yeterlidir:
    mysqldump -h 127.0.0.1 -P 3307 magaza siparis > /kurtarma/siparis-duzeltilmis.sql
    

    Üretime aktarmadan önce mevcut tabloyu yeniden adlandırarak sakla; iş ters giderse geri dönebilecek bir noktan olsun.

    GTID Kullanan Kurulumlarda PITR#

    GTID (Global Transaction Identifier) açık olan sunucularda her işlem benzersiz bir kimlikle etiketlenir ve konum numaraları yerine bu kimlikler kullanılır. Bu, kurtarmayı bazı yönlerden kolaylaştırır ama bir tuzağı da beraberinde getirir.

    Kolaylık şudur: hangi işlemlerin uygulandığını dosya ve konum takip etmeden bilebilirsin. gtid_executed değişkeni sunucunun şimdiye kadar çalıştırdığı tüm işlem kimliklerini tutar:

    SELECT @@GLOBAL.gtid_executed;
    -- 3e11fa47-71ca-11e1-9e33-c80aa9429562:1-284517
    

    Kurtarmada belirli bir aralığı hariç tutmak için --exclude-gtids kullanabilirsin; hatalı işlemin kimliğini bulup yalnızca onu atlamak, konum hesaplamaktan çok daha kesin bir yöntemdir:

    mysqlbinlog --exclude-gtids='3e11fa47-71ca-11e1-9e33-c80aa9429562:284510' \
      /kurtarma/binlogs/mysql-bin.00004[7-9] > /kurtarma/replay.sql
    

    Tuzak ise şudur: GTID açık bir sunucuya, kimliği zaten gtid_executed içinde bulunan bir işlemi tekrar uygulamaya çalışırsan sunucu onu sessizce atlar. Kurtarma örneğine yedeği geri yüklediğinde GTID durumu da geri gelir ve tekrar oynatmak istediğin işlemler "zaten uygulandı" sayılabilir. Bu durumda ya kurtarma örneğinde RESET MASTER ile GTID geçmişini temizlersin, ya da mysqlbinlog çıktısını --skip-gtids ile üretirsin:

    mysqlbinlog --skip-gtids --start-position=8842176 --stop-position=41982 \
      /kurtarma/binlogs/mysql-bin.00004[7-9] > /kurtarma/replay.sql
    

    Sık Yapılan Hatalar ve Kurtarma Tuzakları#

    Birinci ve en yaygın hata, panikle üretim sunucusuna müdahale etmektir. Bir veri kaybı fark edildiğinde ilk refleks tabloyu düzeltmeye çalışmaktır; oysa her yeni yazma, kurtarma penceresini daha da karmaşık hâle getirir. Doğru ilk hamle, uygulamayı bakım moduna almak ve mevcut binlog dosyalarını başka bir yere kopyalamaktır. Sonra sakin sakin ayrı bir örnekte çalış.

    İkinci hata, binlog dosyalarının silinmesine izin vermektir. binlog_expire_logs_seconds süresi dolduğunda MySQL eski dosyaları kendiliğinden siler; kurtarma sırasında sunucu çalışmaya devam ederse ihtiyacın olan dosya gözünün önünde yok olabilir. Kopyalamayı ilk adım yapmanın sebebi budur. Aynı şekilde PURGE BINARY LOGS komutunu kurtarma bitmeden asla çalıştırma.

    Üçüncü hata, STATEMENT formatındaki binlog'a güvenmektir. Eski kurulumlarda hâlâ karşılaşılıyor ve INSERT ... SELECT gibi sorgular ya da NOW() içeren ifadeler yeniden oynatıldığında farklı sonuç üretebiliyor. Kurtarmanın güvenilir olması için ROW formatı şarttır; bunu bugün ayarlarsan yarınki kaza için hazır olursun.

    Dördüncü hata, kurtarmayı hiç prova etmemektir. Yedeğin geri yüklenebildiğini ve binlog'un uygulanabildiğini gerçek bir kaza anında ilk kez denemek, en kötü zamanda sürpriz yaşamak demektir. Üç ayda bir, test sunucusunda baştan sona bir PITR provası yap ve ne kadar sürdüğünü ölç. Bu süre, kurtarma hedefinin (RTO) gerçekçi olup olmadığını gösteren tek dürüst rakamdır. Veritabanı motorunun kendisi bozulduysa ve binlog uygulanamıyorsa MySQL veritabanı onarma yazısındaki adımlar farklı bir yol izler; iki senaryoyu birbirine karıştırma.

    Sıkça Sorulan Sorular#

    Point-in-time recovery ne kadar sürer#

    Süre iki bileşenden oluşur: tam yedeğin geri yükleme süresi ve binlog'un yeniden oynatılma süresi. Birkaç gigabaytlık bir veritabanında toplam işlem genellikle yarım saatin altında biter, ancak yüz gigabaytlık bir sette geri yükleme tek başına saatler alabilir. Binlog oynatma süresi ise yedekten bu yana biriken değişiklik miktarına bağlıdır; bu yüzden yedek sıklığını artırmak kurtarma süresini doğrudan kısaltır.

    Binlog açık değilse silinen veriyi geri getirebilir miyim#

    Hayır. Binlog kapalıysa yapılan değişikliklerin hiçbir kaydı tutulmamıştır ve geri dönebileceğin tek nokta elindeki en son yedektir. Bu durumda yedeğin alındığı andan itibaren girilen tüm veriyi kaybedersin. Bu yüzden binlog'u açmak, veri değeri olan her kurulumda ilk gün yapılması gereken bir ayardır ve neredeyse hiç maliyeti yoktur.

    Sadece bir tabloyu kurtarmak mümkün mü#

    Mümkün ama dolaylı yoldan. mysqlbinlog aracının --database seçeneği veritabanı bazında filtreleme yapar, tablo bazında doğrudan bir filtre yoktur. Pratik yöntem, kurtarmayı ayrı bir MySQL örneğinde tam olarak yapmak, sonra yalnızca ihtiyacın olan tabloyu mysqldump ile alıp üretime aktarmaktır. Bu yaklaşım aynı zamanda üretim verisini riske atmadan sonucu doğrulama imkânı da verir.

    Binlog ne kadar süre saklanmalı#

    Genel kural, en az iki tam yedek döngüsü kadar saklamaktır. Günlük yedek alıyorsan yedi günlük saklama rahat bir pencere sağlar; haftalık yedek alıyorsan en az on dört gün tutmalısın. Süreyi belirlerken disk kapasitesini de hesaba kat: yoğun yazma alan bir sistemde binlog günde onlarca gigabayt üretebilir. Bu yüzden binlog dizinini ayrı bir disk bölümüne koymak iyi bir alışkanlıktır.

    mysqlbinlog çıktısında satırları neden okuyamıyorum#

    ROW formatında satır olayları binlog'a base64 kodlanmış olarak yazılır ve varsayılan çıktıda okunabilir görünmezler. Bunları çözmek için komuta --base64-output=DECODE-ROWS --verbose parametrelerini eklemen gerekir; bu iki parametre birlikte, her satır değişikliğini yorum satırı hâlinde okunabilir SQL olarak yazdırır. Not olarak, bu çıktı yalnızca incelemek içindir ve doğrudan çalıştırılamaz.

    Replikam varken yine de PITR gerekli mi#

    Evet, çünkü replika bir yedek değildir. Yanlışlıkla çalıştırılan bir DELETE komutu saniyeler içinde replikaya da uygulanır ve orada da veri gider. Replika donanım arızasına karşı korur, insan hatasına karşı korumaz. PITR ise tam olarak insan hatasına karşı tasarlanmıştır; ikisini birbirinin alternatifi değil, tamamlayıcısı olarak düşünmek gerekir.

    Kapanış#

    Point-in-time recovery, veri kaybını "ne kadarını kaybettik" sorusundan "hangi saniyeye dönelim" sorusuna indirger, ama bunun için hazırlığın kaza olmadan önce yapılmış olması gerekir. Aklında kalması gereken dört alışkanlık şu: binlog'u ROW formatında ve yedekleme sıklığından uzun bir saklama süresiyle açık tut, her yedeğin yanına binlog dosya adı ve konumunu yaz, bir kaza anında ilk iş olarak binlog dosyalarını kopyala ve kurtarmayı daima ayrı bir örnekte yap. Bir de yılda birkaç kez gerçek bir prova yap; işleyen bir kurtarma planı, yalnızca denenmiş olandır.

    Yedeklerini sunucu dışında güvenli bir kopyada tutmak istiyorsan yedekleme hizmetimiz zamanlanmış kopyalama işini üstlenir. Kurtarma provalarını rahatça yapabileceğin ayrı bir ortam için VDS veya bulut sunucu paketlerimizi kullanabilir, binlog yapılandırması ve kurtarma senaryolarını uzman gözüyle kurgulamak istersen sunucu yönetimi hizmetimizden yararlanabilirsin.

    BinlogKurtarmaMySQL

    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.