← blog · 2 Ekim 2026

PostgreSQL'de Zamanda Geri Dönüş: WAL Arşivleme, pgBackRest ve Geri Yükleme Tuzakları

Gecelik pg_dump bir günlük veri kaybı demektir. Fiziksel yedek ve kesintisiz WAL arşiviyle veritabanını hatalı sorgudan bir saniye önceye döndürmek, araç seçimi, pgBackRest kurulumu ve diski dolduran kırık arşivden saat dilimi kaymasına kadar tuzaklar.

Gece alınan bir pg_dump yedeği, öğleden sonra yanlışlıkla çalıştırılan bir DELETE karşısında size en fazla dünkü veriyi verir. Arada geçen on beş saatin siparişleri, kayıtları ve ödemeleri kaybolur. Zamanda belirli bir ana geri dönüş (point-in-time recovery, PITR) bu boşluğu kapatır: veritabanını "hatalı sorgudan bir saniye önceki" duruma getirebilirsiniz. Bunun için mantıksal döküm değil, fiziksel yedek ve kesintisiz WAL arşivi gerekir. Bu yazıda yaklaşımları karşılaştırıyor, pgBackRest ile çalışan bir kurulum gösteriyor ve en çok can yakan tuzakları anlatıyoruz.

PITR nasıl çalışır

PostgreSQL her değişikliği veri dosyalarına yazmadan önce önceden yazma günlüğüne (WAL) yazar. Elinizde iki şey varsa geçmişteki herhangi bir anı yeniden kurabilirsiniz:

  1. Veri dizininin bir fiziksel kopyası (temel yedek, base backup)
  2. O yedeğin başladığı andan hedeflediğiniz ana kadar üretilmiş bütün WAL dosyaları

Geri yükleme sırasında PostgreSQL temel yedeği açar, ardından WAL kayıtlarını sırayla yeniden oynatır ve verdiğiniz hedefte durur. Zincirde tek bir WAL dosyası eksikse o noktadan sonrasına geçemezsiniz. Bu yüzden PITR'ın gerçek konusu yedek almak değil, WAL arşivinin kopmadan sürmesidir.

pg_dump bu modelin dışındadır. Mantıksal döküm, tek bir anın SQL karşılığıdır; üzerine WAL oynatılamaz. Sürüm yükseltmek, tek bir tabloyu taşımak ya da başka bir ortama veri kopyalamak için hâlâ çok değerlidir, ama PITR'ın yerini tutmaz.

Üç yaklaşım

archive_command ile elle kopyalama ve pg_basebackup. PostgreSQL'in kendi araçlarıyla yapılabilir: WAL dosyalarını bir betikle başka bir yere kopyalarsınız, temel yedeği pg_basebackup ile alırsınız. Öğretici bir başlangıçtır ama üretimde önerilmez. Saklama süresi, eski yedeklerin silinmesi, sıkıştırma, şifreleme, paralel kopyalama ve en önemlisi arşivin bütünlük denetimi sizin betiğinize kalır. archive_command içindeki basit bir cp, hedef dosya zaten varsa ne yapacağını, disk dolduğunda nasıl davranacağını bilmez.

WAL-G. Nesne depolamaya (S3 uyumlu, GCS, Azure) yazmak için tasarlanmış, tek ikili dosya hâlinde gelen, hızlı bir araç. Kubernetes üzerinde ya da tamamen bulut depolamaya dayalı ortamlarda kurulumu çok hafiftir. Yapılandırması ortam değişkenleriyle yapılır, bu da konteyner dünyasına iyi uyar.

pgBackRest. Tam, fark (differential) ve artımlı (incremental) yedek, paralel sıkıştırma, yedek doğrulama, saklama politikası, birden fazla depo ve şifreleme bir arada gelir. check komutu arşivin gerçekten çalıştığını uçtan uca sınar.

Taraf tutmamız gerekirse: sanal makine ya da fiziksel sunucuda çalışan, büyüklüğü onlarca gigabaytı aşan bir veritabanı için pgBackRest seçin. Saklama ve doğrulama mantığının hazır gelmesi, kendi betiğinizi yazmanın getireceği sessiz hataları ortadan kaldırır. Veritabanınızı Kubernetes üzerinde bir operatörle çalıştırıyorsanız, operatörün kendi yedek mekanizmasını kullanın; araç seçimini operatör zaten yapmıştır ve ona karşı ikinci bir mekanizma kurmak çakışma doğurur.

pgBackRest ile kurulum

Önce depo ve küme tanımı. pgBackRest'te her PostgreSQL kümesine "stanza" denir:

[global]
repo1-path=/var/lib/pgbackrest
repo1-retention-full=2
repo1-cipher-type=aes-256-cbc
repo1-cipher-pass=uzun-ve-rastgele-bir-parola
compress-type=zst
process-max=4
start-fast=y

[main]
pg1-path=/var/lib/postgresql/16/main

repo1-retention-full=2 son iki tam yedeği ve onlara bağlı WAL'ı tutar, daha eskisini kendisi siler. Şifreleme parolasını kaybederseniz yedekleriniz açılamaz; onu veritabanı sunucusundan ayrı bir sır kasasında saklayın.

Ardından postgresql.conf içinde arşivlemeyi açın:

wal_level = replica
archive_mode = on
archive_command = 'pgbackrest --stanza=main archive-push %p'
archive_timeout = 60

archive_mode değişikliği yeniden başlatma ister. Sonra stanza'yı oluşturun, düzeni sınayın ve ilk tam yedeği alın:

sudo -u postgres pgbackrest --stanza=main stanza-create
sudo -u postgres pgbackrest --stanza=main check
sudo -u postgres pgbackrest --stanza=main --type=full backup
sudo -u postgres pgbackrest --stanza=main info

Düzenli işletimde haftada bir tam, her gün fark yedeği almak yaygın ve makul bir başlangıçtır. Fark yedeği son tam yedekten beri değişen dosyaları içerir; artımlı yedek ise bir önceki herhangi bir yedekten beri değişenleri. Artımlı zincir uzadıkça geri yükleme daha fazla parçaya bağımlı hâle gelir.

Geri yükleme

Diyelim ki bir tablo öğleden sonra yanlışlıkla silindi ve olayın dakikasını loglardan biliyorsunuz. Sunucuyu durdurun, sonra hedef zamanı verin:

sudo systemctl stop postgresql
sudo -u postgres pgbackrest --stanza=main --delta \
  --type=time "--target=2026-03-14 14:31:00+03" \
  --target-action=promote restore
sudo systemctl start postgresql

--delta veri dizinini boşaltmak yerine yalnızca farklı olan dosyaları değiştirir; büyük veritabanlarında saatler kazandırır. pgBackRest, PostgreSQL 12 ve sonrasında gereken restore_command ayarını ve kurtarma sinyal dosyasını kendisi yazar.

Tuzaklar

Kırık arşiv diski doldurur. archive_command başarısız döndüğü sürece PostgreSQL ilgili WAL dosyasını silmez. Depo erişilemez olursa, kimlik bilgisi süresi dolarsa ya da şifreleme parolası değişirse WAL dizini büyür ve sonunda veritabanı diski dolduğu için durur. pg_stat_archiver görünümündeki failed_count ve last_failed_time alanlarını izleyin, son başarılı arşivin yaşı belirli bir eşiği geçince alarm üretin.

Sessiz veritabanında RPO uzar. WAL dosyası ancak dolduğunda ya da archive_timeout süresi geçtiğinde arşivlenir. Yazmanın az olduğu bir sistemde archive_timeout ayarlanmamışsa son değişiklikler saatlerce yerel diskte bekleyebilir. Değeri çok küçük tutmak da her seferinde tam boyutlu bir WAL dosyası ürettiği için depoyu şişirir; bir dakika makul bir orta noktadır.

Hedef zaman, saat dilimiyle verilmeli. Zaman damgasını dilimsiz verirseniz sunucunun yerel ayarına göre yorumlanır. Uygulama logları UTC, sunucu başka bir dilimdeyse geri dönüş noktası saatlerce kayar. Hedefi her zaman açık dilimle yazın.

Varsayılan davranış duraklatmaktır. recovery_target_action verilmezse ve sıcak yedek (hot standby) açıksa, PostgreSQL hedefe ulaşınca kurtarmayı duraklatır ve salt okunur kalır. Bu aslında iyi bir özelliktir: veriyi kontrol edip hedef doğruysa pg_wal_replay_resume() ile devam edersiniz. Ama bilmeyen biri "veritabanı açıldı ama yazamıyor" diye saatlerce arıza arar.

Geri yükleme yeni bir zaman çizgisi açar. Promote sonrası küme yeni bir timeline'a geçer. Eski zaman çizgisine ait WAL'lar depoda durmaya devam eder. Aynı depoya yazan ikinci bir sunucuyu (örneğin eski birincili) yanlışlıkla ayağa kaldırırsanız iki farklı geçmiş aynı arşive karışmaya çalışır. Geri yüklemeden sonra eski sunucunun arşiv yazmadığından emin olun ve yeni bir tam yedek alın.

Yedek, aynı makinede yedek değildir. Depoyu veritabanıyla aynı diske koymak donanım arızasında hiçbir şey kurtarmaz. pgBackRest birden fazla depo destekler; birini yerel ve hızlı, diğerini başka bir konumdaki nesne depolamasında tutmak hem hızlı geri dönüşü hem de felaket senaryosunu karşılar.

Denenmemiş geri yükleme bir umuttur. check komutu arşivin çalıştığını söyler, geri yüklemenin çalıştığını söylemez. Düzenli aralıklarla ayrı bir makineye gerçek bir PITR yapın, belirli bir zamana dönüp bilinen bir satırı sorgulayın ve geçen süreyi kaydedin. RTO hedefiniz kâğıt üstündeki sayı değil, bu tatbikatta ölçtüğünüz süredir.

Ne zaman gerekmez

Veritabanı birkaç gigabayt ise ve bir günlük veri kaybı iş açısından kabul edilebilirse, gecelik pg_dump ve onun düzenli olarak geri yüklenip sınanması yeterlidir; PITR'ın işletim yükü kazancından fazla olur. Yönetilen bir veritabanı hizmeti kullanıyorsanız PITR çoğunlukla hazır gelir, kendi kurulumunuzu yapmak yerine saklama süresini ve geri yükleme tatbikatını sağlayıcının araçlarıyla yönetin. Son olarak PITR, mantıksal hatalara karşı bir zaman makinesidir; tek bir tabloyu geri almak için bile bütün kümeyi geri sardığını unutmayın. Böyle durumlarda geri yüklemeyi ayrı bir sunucuya yapıp yalnızca gereken satırları üretim veritabanına taşımak, hepsini geri sarmaktan çok daha güvenlidir.