Merhaba
Önceki SQL Server Always On AG’de Otomatik Yedekleme yazımızda bir Always On Availability Group ortamında standalone bir yedekleme script’inin neden çalışmadığını elle, adım adım çözmüştük. Dört sorun vardı: is_read_only filtresi AG üyesi veritabanlarını eliyor, COPY_ONLY unutulunca zincir kırılıyor, backup preference kontrolünü kendimiz yazmak zorunda kalıyorduk, retention penceresi node’lara bölünüyordu.
Bu yazıda aynı dört soruna, elle yazılmış bir script yerine, SQL Server dünyasında fiilen standart haline gelmiş, yaygın kullanılan bir bakım script’i çözümüyle bakıyoruz. Amaç hazır çözüm de var demek değil. Önceki yazıda elle çözdüğümüz her problemin bu çözümde hangi parametreye karşılık geldiğini göstermek, böylece hem kurulumu yapabilesin hem de altında ne döndüğünü bilesin.
MaintenanceSolution.sql ne kuruyor
Bu çözüm tek bir script olarak dağıtılıyor: MaintenanceSolution.sql. Bunu hedef instance’ta çalıştırdığında dört şey oluşuyor.
CommandLog tablosu. Çalıştırılan her komutun (hangi veritabanı, hangi işlem, ne zaman başladı, ne zaman bitti, başarılı mı) kaydını tutan bir tablo. İzleme bölümünde buraya döneceğiz.
Dört saklı yordam:
DatabaseBackup: Full, differential ve log yedeklerini alan yordam. Bizim ilgileneceğimiz asıl parça bu.DatabaseIntegrityCheck:DBCC CHECKDBçalıştırıyor.IndexOptimize: İndeks bakımı yapıyor.CommandExecute: Diğer üç yordamın kullandığı, komutları çalıştırıpCommandLog‘a yazan ortak katman.
SQL Server Agent job’ları. Script çalıştığında bu dört yordamı çağıran job’lar otomatik oluşturuluyor, ama varsayılan job’lar devre dışı geliyor. Parametreleri kendi ortamına göre ayarlayıp job’ları elle etkinleştirmen gerekiyor. Bu noktaya job’lar yine her node’a kurulur başlığında geri döneceğiz, çünkü önceki yazının üçüncü sorunu burada da geçerliliğini koruyor.
Dört sorunun parametre karşılığı
Önceki yazıdaki dört sorunu tek tek elle çözmüştük. Hazır script’teki karşılığı şöyle:
| Önceki yazıdaki sorun | MaintenanceSolution karşılığı |
|---|---|
is_read_only filtresi AG’de her şeyi eler |
@Databases = 'AVAILABILITY_GROUP_DATABASES' ve @AvailabilityGroups ile veritabanı seçimi AG (Availability Group) üyeliğinden okunur, sys.databases bayraklarından değil |
COPY_ONLY unutulursa zincir kırılır |
@CopyOnly = 'Y', ayrıca @ChangeBackupType ile alınamayan yedek türünde davranış kontrolü |
| Tercih kontrolü elle yazılmalı | Çözüm AG (Availability Group) backup preference’ı zaten dikkate alıyor; @OverrideBackupPreference ile bilinçli olarak ezilebiliyor |
| Retention penceresi node’lara bölünüyor | AG (Availability Group) veritabanlarında klasör yapısı sunucu/instance adı yerine cluster ve AG (Availability Group) adı üzerinden kuruluyor, yani ortak paylaşımda tek pencere oluşuyor |
Availability Group parametreleri
Bu bölümdeki parametreler DatabaseBackup yordamının AG (Availability Group)’ye özgü davranışını belirliyor.
| Parametre | Ne işe yarıyor |
|---|---|
@Databases |
Yedeklenecek veritabanı kümesini seçiyor. AVAILABILITY_GROUP_DATABASES değeri, seçimi sys.databases üzerindeki bayraklardan değil, sunucunun üyesi olduğu AG (Availability Group)’lerin veritabanı listesinden yapıyor. Önceki yazıdaki birinci sorunun doğrudan çözümü bu. |
@AvailabilityGroups |
@Databases = 'AVAILABILITY_GROUP_DATABASES' ile birlikte kullanılıyor, hangi AG’lerin dahil edileceğini sınırlıyor. Boş bırakılırsa sunucudaki tüm AG (Availability Group)’ler kapsanıyor. |
@CopyOnly |
'Y' verilirse yedek COPY_ONLY olarak alınıyor. Bu parametre yoksa yazdığımız zincir kırılma riski aynen geri geliyor; AG (Availability Group)’de differential veya log zincirini bozmamak için full yedeklerde bunu bilinçli seçmek gerekiyor. |
@ChangeBackupType |
'Y' verilirse, seçilen yedek türü ortamda mümkün değilse (örneğin secondary’de differential istendi ama AG (Availability Group) bunu desteklemiyor) yordam otomatik olarak full yedeğe düşüyor. Log yedekleri için ayrıca davranış farkı var; ilerideki bölümde işleyeceğiz. |
@OverrideBackupPreference |
Yordam varsayılan olarak AG (Availability Group)’nin backup preference ayarına (Primary, Secondary only, Prefer secondary, Any replica) uyuyor. 'Y' verilirse bu tercih göz ardı edilip yedek zorla o replika üzerinde alınıyor. Test ortamında Execute job’unu belirli bir node’a bağlamak istediğinde işe yarıyor, ama üretimde dikkatli kullanılmalı. |
Yedek yönetimi parametreleri
Bu parametreler AG (Availability Group)’ye özgü değil, ama üretim ortamında doğru ayarlanmazsa önceki yazıda anlattığımız sorunların bir kısmı farklı şekilde geri geliyor.
| Parametre | Ne işe yarıyor |
|---|---|
@Directory |
Yedeklerin yazılacağı yol. AG (Availability Group) veritabanları için klasör yapısı sunucu adı yerine cluster ve AG (Availability Group) adına göre kuruluyor, dördüncü sorunun çözümü burada gerçekleşiyor. |
@Verify |
'Y' verilirse yedek alındıktan sonra RESTORE VERIFYONLY ile doğrulanıyor. |
@Checksum |
'Y' verilirse yedek alınırken checksum hesaplanıyor, bozuk sayfaların erken yakalanmasını sağlıyor. |
@Compress |
Yedek sıkıştırmasını kontrol ediyor. Default bırakılırsa instance ayarını izliyor. |
@CleanupTime |
Eski yedeklerin ne zaman silineceğini belirliyor, birim saat. Aşağıdaki uyarıya bak. |
@CleanupMode |
Silme davranışını kontrol ediyor. AFTER_BACKUP (varsayılan), yeni yedek başarıyla alındıktan sonra eskiyi siliyor; BEFORE_BACKUP, yeni yedeği almadan önce siliyor. |
@LogToTable |
'Y' verilirse çalıştırılan her komut CommandLog tablosuna yazılıyor. İzleme bölümü için bunu açık tutmak gerekiyor. |
Dikkat: @CleanupTime saat cinsinden
Önceki yazıda @retentionDays = 5 yazmıştık, gün cinsindendi. Hazır script’teki karşılığı @CleanupTime = 120‘dir, çünkü birim gün değil saat. Gün sanıp 5 yazan biri beş günlük değil beş saatlik bir pencerede kalır; bu, üretimde fark edilmesi zor ve fark edildiğinde geç kalınan bir hatadır.
@CleanupTime yalnızca başarılı yedekte siliyor
İkinci fark de önemli: @CleanupTime, yalnızca yeni yedek ve varsa doğrulaması (@Verify) başarılı olduğunda eski dosyaları siliyor. Bizim önceki yazıda kullandığımız xp_delete_file yaklaşımı tarih bazlı çalışıyordu ve yedek başarısız olsa da eski dosyaları silebiliyordu. Bu davranış bu açıdan daha güvenli; karşılaştırma bölümünde buna tekrar döneceğiz.
Üçüncü koruma: point-in-time restore
Üçüncü bir koruma daha var: @CleanupTime, en son full veya differential yedekten daha yeni log dosyalarını silmiyor, point-in-time restore imkanını bozmamak için. Yani retention süresi dolsa bile, o log dosyası hâlâ geçerli bir kurtarma zincirinin parçasıysa silinmiyor.
Bulut depolamaya yedekleme: @URL ve @BackupOptions
Yedekler yerel disk veya UNC paylaşımı dışında doğrudan bulut depolamaya da yazılabiliyor. @URL parametresi hedefi belirliyor, @BackupOptions ise hedefe özel ek ayarları JSON olarak taşıyor.
Azure Blob Storage hedefi:
EXECUTE dbo.DatabaseBackup
@Databases = 'AVAILABILITY_GROUP_DATABASES',
@BackupType = 'FULL',
@URL = 'https://hesapadi.blob.core.windows.net/konteyner',
@Compress = 'Y',
@CompressionAlgorithm = 'ZSTD',
@LogToTable = 'Y';
Amazon S3 uyumlu hedefler için de aynı @URL deseni geçerli; Amazon S3’ün kendisi, Wasabi gibi S3-uyumlu servisler ya da AWS dışı S3 API’si sunan sağlayıcılar için @BackupOptions ile bölge ve kimlik doğrulama gibi ayarlar aktarılıyor:
EXECUTE dbo.DatabaseBackup
@Databases = 'AVAILABILITY_GROUP_DATABASES',
@BackupType = 'FULL',
@URL = 's3://s3.wasabisys.com/kova-adi',
@BackupOptions = '{"s3": {"region":"us-east-1"}}',
@Compress = 'Y',
@LogToTable = 'Y';
Bu yolu seçersen dikkat edilecek nokta, AG (Availability Group) veritabanları için @Directory‘deki Cluster/AG klasör deseninin bulut hedefinde aynı şekilde geçerli olmaması; Bucket/Container içindeki yol yapısını kendin tasarlamak gerekiyor. Ayrıca kimlik bilgilerinin (access key, SAS token vb.) instance üzerinde nasıl saklandığı ayrı bir güvenlik konusu, bu seride işlemiyoruz.
Bu serinin konusu yerel/UNC yedekleme olduğu için bulut hedefini ayrıntılı işlemiyoruz, ama parametrelerin var olduğunu ve nasıl kullanıldığını bilmek, ileride bulut tarafına geçiş kararı verirken işine yarayabilir.
Job’lar yine her node’a kurulur
MaintenanceSolution.sql‘i çalıştırdığında oluşan Agent job’ları yalnızca çalıştırıldığı instance’a kuruluyor. AG (Availability Group)’nin diğer node’larında script’i ayrıca çalıştırıp job’ları orada da oluşturman, parametreleri orada da ayarlaman gerekiyor.
Bu, önceki yazının üçüncü sorunuyla aynı gerçeğin devamı: hazır çözüm backup preference kontrolünü senin yerine yapıyor, ama job dağıtımını yapmıyor. Her node kendi job’unu çalıştırıyor, @OverrideBackupPreference kullanılmadığı sürece preference’a uymayan node’da yordam veritabanını atlıyor (log’da ATLANDI benzeri bir kayıt bırakarak). Yani job her node’da kurulu olmalı, hangi node’un fiilen yedek aldığına AG (Availability Group) karar veriyor.
Gerçek ortamda kurulum ve ilk full backup
Aynı iki node’lu, beş veritabanlı test ortamında sırayla şunları doğrulayacağız:
MaintenanceSolution.sqlkurulumu ve oluşan job listesinin ekran görüntüsü.@Databases = 'AVAILABILITY_GROUP_DATABASES'ile@Execute = 'N'çalıştırıp üretilenBACKUPkomutlarını görmek. Bu adım hiçbir şey almadan sadece üretilecek komutları yazdırıyor, AG filtresinin gerçekten çalıştığını görmek için iyi bir görsel oluyor.- Gerçek bir full backup çalıştırıp klasör yapısını görmek, cluster/AG adlı klasörlerin oluştuğunu göstermek.
CommandLogtablosundan sonuçları çekmek.- Aynı kurulumu ikinci node’a yapıp preference’a uymayan node’da beklenen atlama davranışını görmek.
Bu adımların ekran görüntüleri ve komut çıktıları geldiğinde, önce birlikte doğru olup olmadığına bakıp onayını alacağız, sonra bu yazıyı güncelleyeceğiz.
Bölüm 2’ye köprü
Bu bölüm yalnızca doğru yedek almaya odaklandı: kurulum, AG (Availability Group) parametreleri, yedek yönetimi parametreleri, job dağıtımı. İkinci bölümde bunun üstüne inşa edip önceki yazının kapsamadığı üç alanı kapatacağız: log yedekleri ve AG (Availability Group)’de tek zincir sorunu, differential’ın secondary’de desteklenmemesi, sistem veritabanlarının durumu. Ardından CommandLog ile izlemeye, zamanlama tasarımına ve elle script ile hazır çözüm arasında ne zaman hangisinin tercih edileceğine bakacağız.

