Veritabanı yönetiminde en kritik konulardan biri yedekleme stratejisidir. PostgreSQL bu konuda bize iki temel araç sunar: pg_dump ve pg_basebackup. Her ikisi de güçlü araçlar olmakla birlikte, farklı senaryolar için tasarlanmışlardır. Hangisini ne zaman kullanacağınızı bilmek, felaket anında saatlerce süren veri kaybı ile dakikalar içinde recovery yapabilmek arasındaki farkı belirler. Bu yazıda her iki aracı da derinlemesine inceleyeceğiz, gerçek dünya senaryolarıyla pratik kullanım örnekleri vereceğiz.
pg_dump: Mantıksal Yedekleme
pg_dump, PostgreSQL’in mantıksal yedekleme aracıdır. Veritabanının yapısını (şema) ve verilerini SQL komutları veya özel formatlarda dışa aktarır. Çalışan bir veritabanından tutarlı bir yedek alabilmesi, onu üretim ortamları için son derece kullanışlı kılar.
pg_dump Nasıl Çalışır?
pg_dump, yedekleme sırasında veritabanını kilitlemez. MVCC (Multi-Version Concurrency Control) mekanizmasını kullanarak işlem başladığı andaki veri görüntüsünü alır. Bu sayede kullanıcılar veritabanını kullanmaya devam ederken siz yedek alabilirsiniz.
Temel Kullanım
En basit haliyle bir veritabanını yedeklemek:
pg_dump -U postgres -d mydb -f /backup/mydb.sql
Ancak production ortamlarında bu kadar basit bırakmayın. Şu parametreleri mutlaka öğrenin:
-U: Bağlanacak kullanıcı adı -h: Veritabanı sunucusu adresi -p: Port numarası (varsayılan 5432) -d: Yedeklenecek veritabanı adı -f: Çıktı dosyasının yolu -F: Çıktı formatı (p=plain, c=custom, d=directory, t=tar) -j: Paralel iş sayısı (directory formatında kullanılır) -v: Verbose mod, işlem detaylarını gösterir -Z: Sıkıştırma seviyesi (0-9)
Format Seçimi: Hangisini Kullanmalısınız?
Plain format (-Fp): Düz SQL dosyası üretir. İnsan tarafından okunabilir ama büyük veritabanlarında çok yer kaplar.
Custom format (-Fc): Sıkıştırılmış, pg_restore ile seçici geri yükleme yapılabilir. Çoğu senaryo için önerilen format budur.
Directory format (-Fd): Her tablo için ayrı dosya oluşturur, paralel yedekleme/geri yükleme mümkündür.
Tar format (-Ft): Taşınabilir ama sınırlı kullanım alanı var.
# Custom format ile yedekleme (önerilen)
pg_dump -U postgres -d mydb -Fc -Z 9 -f /backup/mydb_$(date +%Y%m%d_%H%M%S).dump
# Directory format ile paralel yedekleme (büyük veritabanları için)
pg_dump -U postgres -d mydb -Fd -j 4 -f /backup/mydb_dir_$(date +%Y%m%d)
Gerçek Dünya Senaryosu 1: E-ticaret Veritabanı Günlük Yedeği
Diyelim ki 50 GB’lık bir e-ticaret veritabanınız var. Her gece 02:00’de tam yedek almak ve 7 gün tutmak istiyorsunuz:
#!/bin/bash
# /usr/local/bin/pg_backup_daily.sh
BACKUP_DIR="/backup/postgresql"
DB_NAME="ecommerce_db"
DB_USER="postgres"
RETENTION_DAYS=7
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_FILE="${BACKUP_DIR}/${DB_NAME}_${DATE}.dump"
LOG_FILE="/var/log/pg_backup.log"
# Backup dizini yoksa oluştur
mkdir -p ${BACKUP_DIR}
echo "[$(date)] Yedekleme başlıyor: ${DB_NAME}" >> ${LOG_FILE}
# Yedekleme al
pg_dump -U ${DB_USER}
-d ${DB_NAME}
-Fc
-Z 6
-v
-f ${BACKUP_FILE} 2>> ${LOG_FILE}
if [ $? -eq 0 ]; then
echo "[$(date)] Yedekleme başarılı: ${BACKUP_FILE}" >> ${LOG_FILE}
# Eski yedekleri temizle
find ${BACKUP_DIR} -name "${DB_NAME}_*.dump" -mtime +${RETENTION_DAYS} -delete
echo "[$(date)] Eski yedekler temizlendi (${RETENTION_DAYS} günden eski)" >> ${LOG_FILE}
else
echo "[$(date)] HATA: Yedekleme başarısız!" >> ${LOG_FILE}
# Burada alert/bildirim sisteminizi çağırabilirsiniz
exit 1
fi
Bu scripti crontab’a ekleyin:
# crontab -e
0 2 * * * /usr/local/bin/pg_backup_daily.sh
Seçici Yedekleme: Sadece İhtiyacınız Olanı Alın
pg_dump‘ın güzel özelliklerinden biri seçici yedekleme yapabilmesidir:
# Sadece belirli tabloları yedekle
pg_dump -U postgres -d mydb -Fc
-t orders
-t order_items
-t customers
-f /backup/sales_tables.dump
# Belirli şemayı yedekle
pg_dump -U postgres -d mydb -Fc
-n public
-f /backup/public_schema.dump
# Sadece şema yapısını al, veri olmadan
pg_dump -U postgres -d mydb -s
-f /backup/schema_only.sql
# Sadece veriyi al, şema olmadan
pg_dump -U postgres -d mydb -a
-f /backup/data_only.sql
pg_restore ile Geri Yükleme
Custom veya directory formatında alınan yedekleri pg_restore ile geri yüklersiniz:
# Tam geri yükleme
pg_restore -U postgres
-d mydb_restored
-v
/backup/mydb_20240115_020000.dump
# Paralel geri yükleme (hızlı!)
pg_restore -U postgres
-d mydb_restored
-j 8
-v
/backup/mydb_20240115_020000.dump
# Sadece belirli tabloları geri yükle
pg_restore -U postgres
-d mydb
-t orders
/backup/mydb_20240115_020000.dump
pg_dumpall: Tüm Cluster’ı Yedekleme
pg_dump tek bir veritabanını yedekler. Global nesneleri (roller, tablespace’ler) ve tüm veritabanlarını yedeklemek için pg_dumpall kullanın:
# Tüm cluster'ı yedekle (globals dahil)
pg_dumpall -U postgres -f /backup/all_databases_$(date +%Y%m%d).sql
# Sadece global nesneleri yedekle (roller, tablespace'ler)
pg_dumpall -U postgres -g -f /backup/globals_$(date +%Y%m%d).sql
pg_basebackup: Fiziksel Yedekleme
pg_basebackup, PostgreSQL cluster’ının fiziksel bir kopyasını alır. Dosya sistemi seviyesinde çalışır ve genellikle Point-in-Time Recovery (PITR) ve streaming replication kurulumlarında kullanılır.
pg_dump’tan Farkı Nedir?
pg_dump verileri SQL veya özel formatta dışa aktarırken, pg_basebackup PostgreSQL’in data dizininin birebir kopyasını alır. Bu kopya, WAL (Write-Ahead Log) dosyalarıyla birleştirildiğinde herhangi bir noktaya geri dönmeyi mümkün kılar.
pg_basebackup‘ın avantajları:
- Çok büyük veritabanlarında
pg_dump‘tan çok daha hızlı - PITR için gerekli altyapıyı sağlar
- Streaming replication standby sunucu kurulumunda kullanılır
- Tüm cluster’ı (tüm veritabanları dahil) tek seferde yedekler
Dezavantajları:
pg_dump‘ın aksine seçici yedekleme yapamazsınız- Aynı büyük versiyon (major version) PostgreSQL ile restore edilmesi gerekir
- WAL arşivleme olmadan alındığında yalnızca o anki tutarlı durumu yansıtır
postgresql.conf Hazırlığı
pg_basebackup kullanmadan önce sunucunuzun WAL arşivlemeye hazır olması gerekir:
# postgresql.conf'ta şu ayarları yapın
wal_level = replica
archive_mode = on
archive_command = 'cp %p /archive/wal/%f'
max_wal_senders = 3
Ayrıca pg_hba.conf‘a replication erişimi ekleyin:
# pg_hba.conf
# TYPE DATABASE USER ADDRESS METHOD
local replication postgres peer
host replication replicator 192.168.1.0/24 md5
Temel Kullanım
# Basit base backup
pg_basebackup -U postgres
-D /backup/basebackup_$(date +%Y%m%d)
-Fp
-Xs
-P
# Tar formatında, sıkıştırılmış
pg_basebackup -U postgres
-D /backup/basebackup_$(date +%Y%m%d)
-Ft
-z
-Xs
-P
-v
Önemli parametreler:
-D: Yedek dosyalarının yazılacağı dizin -F: Format, p=plain (dizin), t=tar -X: WAL dosyalarını dahil etme yöntemi, s=stream (önerilen), f=fetch -z: Gzip sıkıştırma (tar formatıyla birlikte) -P: İlerleme durumunu göster -R: recovery.conf dosyasını otomatik oluştur (standby kurulumu için) -C: Replication slot oluştur –checkpoint: fast veya spread, hızlı checkpoint ister
Gerçek Dünya Senaryosu 2: Standby Sunucu Kurulumu
Streaming replication için standby sunucuya base backup alıp taşıma:
#!/bin/bash
# Standby sunucuda çalıştırılacak script
# /usr/local/bin/setup_standby.sh
MASTER_HOST="192.168.1.10"
REPLICATION_USER="replicator"
PGDATA="/var/lib/postgresql/14/main"
BACKUP_DIR="/tmp/basebackup"
# Mevcut data dizinini temizle (DİKKATLİ OLUN!)
systemctl stop postgresql
rm -rf ${PGDATA}/*
echo "Base backup alınıyor..."
pg_basebackup -h ${MASTER_HOST}
-U ${REPLICATION_USER}
-D ${PGDATA}
-Fp
-Xs
-R
-P
-v
# -R parametresi otomatik olarak standby.signal ve
# postgresql.auto.conf dosyalarını oluşturur
# İzinleri düzelt
chown -R postgres:postgres ${PGDATA}
chmod 700 ${PGDATA}
echo "Standby sunucu hazır. PostgreSQL başlatılıyor..."
systemctl start postgresql
Gerçek Dünya Senaryosu 3: PITR ile Belirli Bir Zamana Dönme
Üretimde bir geliştirici yanlışlıkla kritik bir tabloyu truncate etti. Saat 14:35’te oldu. Siz günlük base backup ve WAL arşivleme yapıyorsunuz. 14:34’e dönmeniz gerekiyor:
# 1. Mevcut PostgreSQL servisini durdur
systemctl stop postgresql
# 2. Yeni bir recovery dizini oluştur
mkdir -p /recovery/data
# 3. Base backup'ı bu dizine aç
tar -xzf /backup/basebackup_20240115/base.tar.gz -C /recovery/data/
tar -xzf /backup/basebackup_20240115/pg_wal.tar.gz -C /recovery/data/pg_wal/
# 4. recovery.conf yerine postgresql.conf'a recovery ayarlarını ekle
# (PostgreSQL 12+)
cat >> /recovery/data/postgresql.conf << EOF
restore_command = 'cp /archive/wal/%f %p'
recovery_target_time = '2024-01-15 14:34:00'
recovery_target_action = 'promote'
EOF
# 5. recovery.signal dosyası oluştur (PostgreSQL 12+)
touch /recovery/data/recovery.signal
# 6. İzinleri düzelt
chown -R postgres:postgres /recovery/data
chmod 700 /recovery/data
# 7. PostgreSQL'i recovery modunda başlat
# PGDATA'yı geçici olarak değiştir
PGDATA=/recovery/data postgresql -D /recovery/data
# Log'ları takip ederek recovery'nin tamamlanmasını bekleyin
tail -f /recovery/data/log/postgresql-*.log
pg_basebackup ile Düzenli Yedekleme Scripti
#!/bin/bash
# /usr/local/bin/basebackup_weekly.sh
# Her Pazar gecesi çalışacak haftalık base backup
BACKUP_BASE="/backup/basebackup"
RETENTION_WEEKS=4
DATE=$(date +%Y%m%d)
BACKUP_DIR="${BACKUP_BASE}/backup_${DATE}"
LOG_FILE="/var/log/pg_basebackup.log"
echo "[$(date)] Base backup başlıyor..." >> ${LOG_FILE}
mkdir -p ${BACKUP_DIR}
pg_basebackup -U postgres
-D ${BACKUP_DIR}
-Ft
-z
-Xs
-P
-v
--checkpoint=fast 2>> ${LOG_FILE}
if [ $? -eq 0 ]; then
echo "[$(date)] Base backup başarılı: ${BACKUP_DIR}" >> ${LOG_FILE}
# Dosya boyutunu logla
BACKUP_SIZE=$(du -sh ${BACKUP_DIR} | cut -f1)
echo "[$(date)] Yedek boyutu: ${BACKUP_SIZE}" >> ${LOG_FILE}
# Eski yedekleri temizle
ls -dt ${BACKUP_BASE}/backup_* | tail -n +$((RETENTION_WEEKS + 1)) | xargs rm -rf
echo "[$(date)] Eski yedekler temizlendi" >> ${LOG_FILE}
else
echo "[$(date)] HATA: Base backup başarısız!" >> ${LOG_FILE}
exit 1
fi
İki Yöntemi Birlikte Kullanma: Kapsamlı Yedekleme Stratejisi
Gerçek dünyada bu iki yöntemi birbirinin alternatifi olarak değil, tamamlayıcı olarak kullanmalısınız.
Önerilen Strateji
- Her gece:
pg_dumpile kritik veritabanlarının mantıksal yedeğini alın. Hızlı geri yükleme ve seçici recovery için idealdir. - Her hafta:
pg_basebackupile tam fiziksel yedek alın. PITR altyapısını destekler. - Sürekli: WAL arşivleme aktif olsun. Bu sayede haftalık backup arasındaki herhangi bir noktaya dönebilirsiniz.
# Kapsamlı yedekleme stratejisi için crontab
# Her gece 01:00'de mantıksal yedek
0 1 * * * /usr/local/bin/pg_backup_daily.sh
# Her Pazar 03:00'te fiziksel yedek
0 3 * * 0 /usr/local/bin/basebackup_weekly.sh
# WAL arşivleme postgresql.conf'ta aktif (sürekli)
Yedeklerin Bütünlüğünü Test Etme
Yedek almak yetmez, test etmek zorundasınız. Bu adımı atlayan birçok sysadmin’in kritik anlarda “yedek bozuk” sürprizi yaşadığını gördüm:
#!/bin/bash
# /usr/local/bin/backup_verify.sh
# Her hafta yedeklerin sağlamlığını test et
BACKUP_FILE=$(ls -t /backup/postgresql/*.dump | head -1)
TEST_DB="backup_test_$(date +%Y%m%d)"
LOG_FILE="/var/log/backup_verify.log"
echo "[$(date)] Yedek doğrulama başlıyor: ${BACKUP_FILE}" >> ${LOG_FILE}
# Test veritabanı oluştur
psql -U postgres -c "CREATE DATABASE ${TEST_DB};" 2>> ${LOG_FILE}
# Yedeği geri yükle
pg_restore -U postgres
-d ${TEST_DB}
-v
${BACKUP_FILE} 2>> ${LOG_FILE}
if [ $? -eq 0 ]; then
echo "[$(date)] Yedek doğrulama BAŞARILI" >> ${LOG_FILE}
# Temel sorgu testi
TABLE_COUNT=$(psql -U postgres -d ${TEST_DB} -t -c
"SELECT count(*) FROM information_schema.tables WHERE table_schema='public';")
echo "[$(date)] Geri yüklenen tablo sayısı: ${TABLE_COUNT}" >> ${LOG_FILE}
else
echo "[$(date)] HATA: Yedek doğrulama BAŞARISIZ!" >> ${LOG_FILE}
fi
# Test veritabanını temizle
psql -U postgres -c "DROP DATABASE ${TEST_DB};" 2>> ${LOG_FILE}
echo "[$(date)] Doğrulama tamamlandı" >> ${LOG_FILE}
Uzak Sunucuya Yedekleme
Yedekleri yerel diskde tutmak single point of failure oluşturur. Uzak bir sunucuya veya nesne depolama alanına gönderin:
# rsync ile uzak sunucuya kopyalama
rsync -avz --delete
/backup/postgresql/
backup-server:/remote/backup/postgresql/
--log-file=/var/log/rsync_backup.log
# SSH üzerinden direkt pipe ile yedekleme
pg_dump -U postgres -d mydb -Fc |
ssh backup-user@backup-server
"cat > /backup/mydb_$(date +%Y%m%d).dump"
# AWS S3'e gönderme (aws cli gerekli)
pg_dump -U postgres -d mydb -Fc |
aws s3 cp - s3://my-bucket/postgresql/mydb_$(date +%Y%m%d).dump
Performans İpuçları
Büyük veritabanlarında yedekleme süresi kritik olabilir. Birkaç pratik ipucu:
- Paralel yedekleme kullanın:
pg_dump -Fd -j 8ile 8 paralel iş başlatabilirsiniz. CPU core sayısını aşmayın. - Sıkıştırma seviyesini ayarlayın:
-Z 6genellikle iyi bir denge noktasıdır. Maksimum sıkıştırma için-Z 9ama bu CPU yoğundur. - Ağ yedeklemelerinde bant genişliğini yönetin:
pg_basebackup --max-rate=100Mile hız sınırı koyabilirsiniz. - checkpoint=fast kullanın:
pg_basebackup --checkpoint=fastile uzun checkpoint bekleme süresinden kurtulun. - Off-peak saatlerde çalışın: Yedekleme disk I/O yoğundur, üretim trafiğinin düşük olduğu saatlerde zamanlayın.
Sonuç
pg_dump ve pg_basebackup, birbirini tamamlayan iki güçlü araçtır. pg_dump esnekliği ve taşınabilirliği ile mantıksal yedekleme ihtiyaçlarınızı karşılar; hızlı single-table recovery, farklı PostgreSQL versiyonlarına migration ve küçük-orta ölçekli veritabanları için mükemmeldir. pg_basebackup ise büyük ölçekli ortamlar, PITR gereksinimleri ve replication kurulumları için vazgeçilmezdir.
İdeal bir yedekleme stratejisi ikisini birlikte kullanır: Günlük pg_dump, haftalık pg_basebackup ve sürekli WAL arşivleme. Bu üçlü sizi neredeyse her felaket senaryosuna karşı korur.
Son olarak, en iyi yedekleme senaryosu bile test edilmemişse değersizdir. Yedeklerinizi düzenli olarak test edin, restore prosedürlerinizi belgeleyin ve ekibinizin bu prosedürleri bildiğinden emin olun. Çünkü felaket anında “nasıl geri yükleyeceğimi tam hatırlamıyorum” demek istemezsiniz.