Azure SQL Database Yedekleme ve Geri Yükleme Rehberi

Veritabanı yedekleme meselesi, birçok ekibin “biz zaten cloud’dayız, otomatik halleder” diyerek geçiştirdiği ama ilk ciddi veri kaybında çok pişman olduğu bir konu. Azure SQL Database, gerçekten güçlü bir yedekleme altyapısı sunuyor. Ancak bu altyapıyı anlamadan, hangi seçeneğin ne zaman işe yaradığını bilmeden sadece “default’a bırakırım” demek, prodüksiyon ortamında ciddi riskler doğurabilir. Bu yazıda Azure SQL Database’in yedekleme mekanizmalarını, geri yükleme senaryolarını ve bunu otomatize etmenin pratik yollarını ele alacağız.

Azure SQL Database Yedekleme Mekanizmaları

Azure SQL Database, managed bir servis olduğu için yedekleme işlemlerinin büyük kısmını sizin adınıza Microsoft halleder. Ancak bu “tamamen elleri çekebilirsin” anlamına gelmiyor. Temel yedekleme türlerini iyi anlamak gerekiyor.

Otomatik Yedekleme Türleri

Azure SQL Database üç farklı otomatik yedekleme tipi kullanır:

  • Full Backup: Haftada bir kez alınır. Tüm veritabanının anlık görüntüsüdür.
  • Differential Backup: 12 ila 24 saatte bir alınır. Son full backup’tan bu yana yapılan değişiklikleri içerir.
  • Transaction Log Backup: 5 ila 10 dakikada bir alınır. Bu aralık, RPO (Recovery Point Objective) değerinizi doğrudan belirler.

Bu üçlü kombinasyon sayesinde teorik olarak son 5-10 dakika içindeki herhangi bir noktaya geri dönebilirsiniz. Bu özelliğe Point-in-Time Restore (PITR) deniyor ve en sık kullanılan geri yükleme yöntemi bu.

Retention Period Ayarları

Varsayılan retention süresi 7 gündür. Basic tier için bu süre sabit kalır, ancak General Purpose ve Business Critical tier’larda 1 ile 35 gün arasında ayarlayabilirsiniz. Long-Term Retention (LTR) ile bu süreyi yıllarca uzatabilirsiniz.

Retention süresini Azure CLI ile sorgulamak için:

# Mevcut retention policy'yi görüntüle
az sql db show 
  --resource-group myResourceGroup 
  --server myserver 
  --name myDatabase 
  --query "{retention: backupRetentionDays, edition: edition}"

Retention süresini değiştirmek istediğinizde:

# Retention süresini 14 güne çıkar
az sql db update 
  --resource-group myResourceGroup 
  --server myserver 
  --name myDatabase 
  --backup-storage-redundancy Geo 
  --set backupRetentionDays=14

Point-in-Time Restore (PITR) Kullanımı

PITR, prodüksiyon hayatında en çok kullandığınız geri yükleme yöntemi olacak. Bir geliştirici yanlışlıkla WHERE koşulu olmadan UPDATE çalıştırdı mı? Sabah 03:17’deki duruma dönmeniz gerekiyor mu? PITR tam bunun için var.

Önemli bir nokta: PITR her zaman yeni bir veritabanı oluşturur. Mevcut veritabanının üzerine yazamaz. Bu hem bir güvenlik önlemi hem de pratik bir esneklik. Önce geri yükleme yaparsınız, verify edersiniz, sonra swap işlemi yaparsınız.

# Belirli bir zamana geri yükleme
az sql db restore 
  --dest-name myDatabase-restored 
  --edition GeneralPurpose 
  --family Gen5 
  --capacity 4 
  --resource-group myResourceGroup 
  --server myserver 
  --name myDatabase 
  --time "2024-01-15T03:17:00Z"

Zaman formatına dikkat edin: UTC formatında ISO 8601 kullanmanız gerekiyor. Türkiye saatiyle çalışıyorsanız UTC+3 farkını hesaba katmayı unutmayın. Saat 06:17 Türkiye saatiyse, UTC’ye çevirince 03:17 olur.

Geri yükleme işleminin durumunu takip etmek için:

# Restore işleminin durumunu kontrol et
az sql db show 
  --resource-group myResourceGroup 
  --server myserver 
  --name myDatabase-restored 
  --query "{status: status, createDate: createDate}"

Long-Term Retention (LTR) Politikaları

Yasal uyumluluk gereksinimleri, bazı veritabanlarının yıllarca yedeklerini tutmanızı zorunlu kılabilir. KVKK, GDPR ya da sektörel regülasyonlar bu gereksinimleri doğurabilir. 35 günlük maksimum standart retention bu senaryolar için yetmez. LTR tam burada devreye girer.

LTR politikasını ayarlamak için:

# LTR politikası oluştur
# W = haftalık, M = aylık, Y = yıllık retention
az sql db ltr-policy set 
  --resource-group myResourceGroup 
  --server myserver 
  --name myDatabase 
  --weekly-retention "P4W" 
  --monthly-retention "P12M" 
  --yearly-retention "P5Y" 
  --week-of-year 1

Bu komut şunu yapar:

  • weekly-retention P4W: Haftalık yedekleri 4 hafta tutar
  • monthly-retention P12M: Aylık yedekleri 12 ay tutar
  • yearly-retention P5Y: Yıllık yedekleri 5 yıl tutar
  • week-of-year 1: Yılın 1. haftasını yıllık yedek olarak işaretle

Mevcut LTR yedeklerini listelemek için:

# LTR yedeklerini görüntüle
az sql db ltr-backup list 
  --location eastus 
  --server myserver 
  --database myDatabase 
  --output table

LTR yedeğinden geri yükleme yapmak standart PITR’dan biraz farklı çalışır:

# Önce LTR backup'ın ID'sini bul
BACKUP_ID=$(az sql db ltr-backup list 
  --location eastus 
  --server myserver 
  --database myDatabase 
  --query "[0].id" 
  --output tsv)

# LTR backup'tan geri yükleme yap
az sql db ltr-backup restore 
  --backup-id "$BACKUP_ID" 
  --dest-database myDatabase-ltr-restored 
  --dest-resource-group myResourceGroup 
  --dest-server myserver 
  --edition GeneralPurpose 
  --family Gen5 
  --capacity 4 
  --backup-storage-redundancy Geo

Geo-Restore: Bölgesel Felaket Senaryoları

Bir Azure bölgesinin tamamen çöktüğünü düşünün. Türkiye West bölgesi erişilemez oldu diyelim. Burada PITR işe yaramaz çünkü PITR aynı sunucu üzerinde çalışır. İşte bu senaryolar için Geo-Restore var.

Geo-Restore, yedeklerinizin coğrafi olarak redundant storage (GRS) üzerinde tutulduğu durumlarda, farklı bir Azure bölgesine geri yükleme yapmanıza olanak tanır.

# Farklı bölgedeki backup'ları listele
az sql db geo-backup list 
  --resource-group myResourceGroup 
  --server myserver

# Geo-restore ile farklı bölgeye yükle
az sql db geo-backup restore 
  --georeplicated-backup-id "/subscriptions/SUB_ID/resourceGroups/myResourceGroup/providers/Microsoft.Sql/servers/myserver/recoverableDatabases/myDatabase" 
  --dest-database myDatabase-geo-restored 
  --dest-resource-group myDRResourceGroup 
  --dest-server myDRServer 
  --edition GeneralPurpose 
  --family Gen5 
  --capacity 4

Geo-Restore için şu noktaları aklınızda tutun:

  • RPO yaklaşık 1 saattir: Geo-replicated yedekler anlık değildir, lag yaşanabilir.
  • RTO birkaç saate kadar çıkabilir: Büyük veritabanlarında geri yükleme uzun sürer.
  • Business Critical tier için Active Geo-Replication çok daha iyi bir DR seçeneğidir.

Export ve BACPAC ile Manuel Yedekleme

Bazen otomatik yedekler yetmez. Özellikle büyük bir migration öncesi, deployment öncesi ya da geliştirme ortamına prodüksiyon verisi almak istediğinizde manuel yedek almanız gerekir. BACPAC formatı bu iş için biçilmiş kaftan.

# Veritabanını BACPAC olarak Azure Storage'a export et
az sql db export 
  --resource-group myResourceGroup 
  --server myserver 
  --name myDatabase 
  --admin-user sqladmin 
  --admin-password "MyStrongP@ssw0rd!" 
  --storage-key-type StorageAccessKey 
  --storage-key "YOUR_STORAGE_ACCOUNT_KEY" 
  --storage-uri "https://mystorageaccount.blob.core.windows.net/backups/myDatabase-$(date +%Y%m%d%H%M%S).bacpac"

Export işlemini script içinde kullandığınızda, işlemin tamamlanmasını beklemeniz gerekebilir. Asenkron bir operasyon olduğu için:

#!/bin/bash
# export_and_wait.sh

RESOURCE_GROUP="myResourceGroup"
SERVER="myserver"
DATABASE="myDatabase"
STORAGE_ACCOUNT="mystorageaccount"
CONTAINER="backups"
DATE_SUFFIX=$(date +%Y%m%d%H%M%S)
BACPAC_NAME="myDatabase-${DATE_SUFFIX}.bacpac"

echo "Export başlatılıyor: ${BACPAC_NAME}"

# Export operasyonunu başlat ve operation ID'yi al
OPERATION_ID=$(az sql db export 
  --resource-group "$RESOURCE_GROUP" 
  --server "$SERVER" 
  --name "$DATABASE" 
  --admin-user sqladmin 
  --admin-password "$SQL_PASSWORD" 
  --storage-key-type StorageAccessKey 
  --storage-key "$STORAGE_KEY" 
  --storage-uri "https://${STORAGE_ACCOUNT}.blob.core.windows.net/${CONTAINER}/${BACPAC_NAME}" 
  --query "name" 
  --output tsv)

echo "Operation ID: ${OPERATION_ID}"

# İşlemin tamamlanmasını bekle
while true; do
  STATUS=$(az sql db op show 
    --resource-group "$RESOURCE_GROUP" 
    --server "$SERVER" 
    --database "$DATABASE" 
    --operation "$OPERATION_ID" 
    --query "state" 
    --output tsv 2>/dev/null)
  
  echo "Durum: ${STATUS}"
  
  if [ "$STATUS" = "Succeeded" ]; then
    echo "Export tamamlandı: ${BACPAC_NAME}"
    break
  elif [ "$STATUS" = "Failed" ] || [ "$STATUS" = "Cancelled" ]; then
    echo "Export başarısız! Durum: ${STATUS}"
    exit 1
  fi
  
  sleep 30
done

Otomatik Yedekleme ve Monitoring Script’leri

Operasyonel hayatta yedekleme işlemlerini takip etmek, alerting kurmak kritik önem taşır. Azure Monitor ile yedekleme metrikleri takip edilebilir ama bazen daha basit bir shell script de hayat kurtarır.

#!/bin/bash
# check_backup_health.sh - Backup retention ve son yedek durumunu kontrol et

SUBSCRIPTION_ID="YOUR_SUBSCRIPTION_ID"
RESOURCE_GROUP="myResourceGroup"
SERVER="myserver"
DATABASES=("db1" "db2" "db3")
ALERT_EMAIL="[email protected]"
MIN_RETENTION_DAYS=7

az account set --subscription "$SUBSCRIPTION_ID"

ISSUES_FOUND=0
REPORT=""

for DB in "${DATABASES[@]}"; do
  echo "Kontrol ediliyor: $DB"
  
  # Retention ve backup storage redundancy bilgisi al
  DB_INFO=$(az sql db show 
    --resource-group "$RESOURCE_GROUP" 
    --server "$SERVER" 
    --name "$DB" 
    --query "{retention: backupRetentionDays, redundancy: requestedBackupStorageRedundancy, status: status}" 
    --output json 2>/dev/null)
  
  if [ $? -ne 0 ]; then
    REPORT+="[HATA] $DB veritabanına erişilemiyor!n"
    ISSUES_FOUND=$((ISSUES_FOUND + 1))
    continue
  fi
  
  RETENTION=$(echo "$DB_INFO" | jq -r '.retention')
  REDUNDANCY=$(echo "$DB_INFO" | jq -r '.redundancy')
  STATUS=$(echo "$DB_INFO" | jq -r '.status')
  
  if [ "$STATUS" != "Online" ]; then
    REPORT+="[UYARI] $DB durumu Online değil: $STATUSn"
    ISSUES_FOUND=$((ISSUES_FOUND + 1))
  fi
  
  if [ "$RETENTION" -lt "$MIN_RETENTION_DAYS" ]; then
    REPORT+="[UYARI] $DB retention süresi yetersiz: ${RETENTION} gün (minimum: ${MIN_RETENTION_DAYS})n"
    ISSUES_FOUND=$((ISSUES_FOUND + 1))
  fi
  
  if [ "$REDUNDANCY" != "Geo" ]; then
    REPORT+="[BİLGİ] $DB Geo-redundant backup kullanmıyor: $REDUNDANCYn"
  fi
  
  REPORT+="[OK] $DB - Retention: ${RETENTION}g, Redundancy: $REDUNDANCYn"
done

if [ "$ISSUES_FOUND" -gt 0 ]; then
  echo "Sorunlar tespit edildi, rapor gönderiliyor..."
  echo -e "$REPORT" | mail -s "Azure SQL Backup Health Alert" "$ALERT_EMAIL"
fi

echo -e "n--- RAPOR ---n$REPORT"

Gerçek Dünya Senaryosu: Yanlış UPDATE Sonrası Kurtarma

Şimdi gerçek bir senaryoyu adım adım inceleyelim. Geliştiriciden şu mesajı aldınız: “Abi az önce users tablosunu güncelledim ama WHERE koşulunu unutmuşum. Tüm kullanıcıların email’i aynı oldu. Saat tam 14:23’teydi.”

Aşağıdaki adımları izleyin:

1. Adım: Paniklemeden durumu teyit edin

# Önce hangi veritabanını etkilediğini ve boyutunu öğren
az sql db show 
  --resource-group prod-rg 
  --server prod-server 
  --name prodDB 
  --query "{sizeGB: maxSizeBytes, tier: edition, status: status}"

2. Adım: Hata zamanından önce bir noktayı hedefleyin

# 14:20 UTC zamanına restore et (hata 14:23'te olmuş, 3 dakika marj)
# Türkiye saati 14:20 = UTC 11:20
az sql db restore 
  --dest-name prodDB-rescue-$(date +%Y%m%d) 
  --edition GeneralPurpose 
  --family Gen5 
  --capacity 8 
  --resource-group prod-rg 
  --server prod-server 
  --name prodDB 
  --time "2024-01-15T11:20:00Z"

3. Adım: Restore tamamlandıktan sonra doğrulama yapın

# Restore durumunu kontrol et
az sql db show 
  --resource-group prod-rg 
  --server prod-server 
  --name prodDB-rescue-20240115 
  --query "status"

4. Adım: Veriyi doğruladıktan sonra swap veya seçici kurtarma yapın

Tam veritabanını swap etmek yerine, genellikle etkilenen tabloyu restore edilmiş veritabanından import etmek daha hızlı ve güvenlidir. SQL Server Management Studio veya sqlcmd ile bu işlemi yapabilirsiniz.

5. Adım: Kurtarma sonrası temizlik

# Rescue veritabanını sil (maliyet açısından önemli!)
az sql db delete 
  --resource-group prod-rg 
  --server prod-server 
  --name prodDB-rescue-20240115 
  --yes

Silinen Veritabanını Geri Yükleme

Bir veritabanı yanlışlıkla silindiğinde, retention süresi içinde geri yüklemek mümkün. Azure bu durumu “deleted database backup” olarak tutar.

# Silinen veritabanlarını listele
az sql db list-deleted 
  --resource-group myResourceGroup 
  --server myserver 
  --query "[].{name: databaseName, deletionDate: deletionDate}" 
  --output table

# Silinen veritabanını geri yükle
az sql db restore 
  --dest-name myDatabase-recovered 
  --edition GeneralPurpose 
  --family Gen5 
  --capacity 4 
  --resource-group myResourceGroup 
  --server myserver 
  --name myDatabase 
  --deleted-time "2024-01-14T10:30:00Z" 
  --time "2024-01-14T10:25:00Z"

Yedekleme Maliyetlerini Optimize Etme

Yedekleme storage maliyetleri, görmezden gelinen ama faturada sürpriz yapan kalemlerden biridir. Şu noktalara dikkat edin:

  • Backup Storage Redundancy seçimi kritiktir: LRS (Locally Redundant) en ucuz, GRS (Geo-Redundant) en pahalı. Sadece kritik veritabanları için GRS kullanın.
  • Retention süresini gerektiği kadar tutun: 35 gün tutmak 7 gün tutmaktan 5 kat daha fazla backup storage tüketir.
  • LTR yedekleri için lifecycle policy kurun: Gereksiz eski yedekler silinmeden birikir.
  • Dev/Test veritabanlarında retention’ı düşürün: Prodüksiyon ile aynı politika uygulamak anlamsız maliyet doğurur.
# Dev veritabanı için minimal retention ayarla
az sql db update 
  --resource-group dev-rg 
  --server dev-server 
  --name devDatabase 
  --backup-storage-redundancy Local 
  --set backupRetentionDays=7

Sonuç

Azure SQL Database’in otomatik yedekleme sistemi gerçekten güçlü, ama “Azure halleder” deyip geçmek büyük risk. Her ekibin şu üç şeyi net olarak bilmesi gerekiyor: RPO ve RTO hedefleri ne, hangi tier hangi yedekleme kapasitesini sunuyor ve geri yükleme prosedürü tam olarak nasıl işliyor.

PITR günlük operasyonlarda hayat kurtarır. LTR yasal uyumluluk için şarttır. Geo-restore bölgesel felaket senaryolarında son savunma hattınızdır. BACPAC ise migration ve dev/test ortamları için vazgeçilmez.

En önemli kural şu: Yedek aldığınızı değil, yedeği geri yükleyebildiğinizi test edin. Her ay bir restore drill yapın, süreci belgeleyin ve ekibinizin gece 3’te bile bu işlemi yapabilmesini sağlayın. Çünkü prodüksiyon krizi her zaman en kötü saatte gelir.

Bir yanıt yazın

E-posta adresiniz yayınlanmayacak. Gerekli alanlar * ile işaretlenmişlerdir