Zero-Downtime Database Migration: Canlı Sistemlerde Kesintisiz Veritabanı Güncelleme Stratejileri
Veritabanı şeması değişiklikleri (schema migrations), yazılım yaşam döngüsünün en riskli ve en zorlu operasyonlarından biridir. Geleneksel yaklaşımda, uygulamayı durdurup (maintenance mode), veritabanını güncellemek ve ardından uygulamayı yeniden başlatmak, günümüzün 7/24 çalışan sistemleri için kabul edilemez bir durumdur. Zero-Downtime (Sıfır Kesinti) Veritabanı Geçişi, bu riski ortadan kaldırmak için kullanılan bir dizi strateji ve deseni ifade eder.
Bu yazıda, veritabanı şemasını canlı sistemlerde kesintisiz olarak nasıl güncelleyeceğinizi, Expand/Contract (Genişlet/Sıkıştır) desenini, Backward-Compatible (Geriye Dönük Uyumlu) migration'ları ve bu süreci otomatikleştiren araçları (Flyway, DbUp) inceleyeceğiz. Ayrıca, .NET/EF Core ile bu stratejilerin nasıl uygulanacağına dair pratik ipuçları vereceğiz.
1. Neden Zero-Downtime Migration? Geleneksel Yaklaşımın Riskleri
Geleneksel veritabanı güncelleme süreci genellikle şu şekildedir:
-
Uygulamayı durdur (Maintenance mode).
-
Veritabanı yedeklemesi al.
-
SQL script'lerini çalıştır (ALTER TABLE, ADD COLUMN, vb.).
-
Uygulamayı yeniden başlat.
Bu yöntem, küçük projeler için çalışabilir, ancak büyük ve kritik sistemlerde aşağıdaki riskleri taşır:
-
Uzun Süreli Kesinti (Downtime): Büyük tablolarda
ALTER TABLEişlemleri saatler sürebilir. -
Riskli Geri Dönüş (Rollback): Hata durumunda, veritabanını eski haline döndürmek (restore) uzun sürer ve veri kaybına neden olabilir.
-
Kullanıcı Memnuniyetsizliği: 7/24 hizmet veren uygulamalarda kesinti kabul edilemez.
Zero-Downtime Migration, bu riskleri en aza indirir ve uygulamanın çalışmaya devam etmesini sağlar.
2. Expand/Contract (Genişlet/Sıkıştır) Deseni
Bu desen, veritabanı şeması değişikliklerini birden fazla aşamaya (phase) bölerek uygulamak ve eski/yeni kod sürümlerinin aynı anda çalışabilmesini sağlamak üzerine kuruludur.
Aşama 1: Expand (Genişlet) - Yeni Sütun veya Tablo Ekleme
-
Yeni bir sütun (veya tablo) ekleyin, ancak bu sütunun boş (nullable) veya varsayılan bir değeri olduğundan emin olun.
-
Yeni kodu (yeni sürüm) dağıtın. Yeni kod, hem eski sütunu hem de yeni sütunu kullanabilir.
-
Eski kod (eski sürüm) çalışmaya devam eder ve bu değişiklikten etkilenmez (çünkü yeni sütun, eski kod tarafından görmezden gelinir).
Aşama 2: Contract (Sıkıştır) - Eski Kodu Devre Dışı Bırakma ve Sütunu Zorunlu Yapma
-
Tüm uygulama instance'ları yeni sürüme geçtikten (eski kod kalmadıktan) sonra, yeni sütunu doldurmak için bir veri taşıma işlemi (backfill) yapılır.
-
Veriler taşındıktan sonra, yeni sütun
NOT NULLyapılabilir (eğer isteniyorsa) veya eski sütun kaldırılabilir (drop column). -
Eğer eski sütun kaldırılıyorsa, bu işlem en son aşamada yapılmalıdır ve eski kodun artık bu sütuna ihtiyacı olmadığından emin olunmalıdır.
Örnek: Bir sütunu yeniden adlandırmak (rename) veya tipini değiştirmek için Expand/Contract:
-
Expand: Yeni sütunu (
new_name) ekleyin. Verileri eski sütundan (old_name) yeni sütuna kopyalayın. -
Contract (2): Kodu, yeni sütunu (
new_name) kullanacak şekilde güncelleyin ve dağıtın. -
Contract (3): Eski sütunu (
old_name) kaldırın.
3. Backward-Compatible (Geriye Dönük Uyumlu) Migration'lar
Bir migration'ın backward-compatible (geriye dönük uyumlu) olması, eski kod sürümünün, yeni şema ile çalışmaya devam edebilmesi anlamına gelir. Bu, zero-downtime için kritiktir.
Başarılı (Zero-Downtime) Migration Kriterleri:
-
Yeni Sütunlar
NULLABLEveya Varsayılan Değerli Olmalıdır:-
Eski kod, yeni sütunu görmezden gelir. Yeni sütuna bir değer yazılmazsa,
NULLveya varsayılan değer kalır.NOT NULLbir sütun eklemek, eski kodunINSERTişlemlerini kırar.
-
-
Sütun Silme İşlemi En Son Yapılmalıdır:
-
Önce kodu, sütunu kullanmayacak şekilde güncelleyin. Tüm instance'lar yeni koda geçtikten sonra sütunu silin.
-
-
Tablo veya Sütun Yeniden Adlandırma (Rename) İki Aşamada Yapılmalıdır:
-
Yeni adı ekleyin, eski adı koruyun (çift yazma/okuma). Ardından kodu güncelleyip, eski adı kaldırın.
-
-
İndeks Değişiklikleri Dikkatli Yapılmalıdır:
-
Yeni bir indeks eklemek (
CREATE INDEX CONCURRENTLYile) genellikle güvenlidir. Eski bir indeksi kaldırmak, sorgu performansını etkileyebilir, bu nedenle dikkatli olunmalıdır.
-
Sık Yapılan Hata: NOT NULL Sütun Eklemek:
sql
-- KÖTÜ! Bu işlem eski kodun INSERT çalıştırmasını engeller. ALTER TABLE orders ADD COLUMN status INT NOT NULL;
Doğrusu:
sql
-- Önce NULLABLE ekle, verileri doldur, sonra NOT NULL yap. ALTER TABLE orders ADD COLUMN status INT NULL; -- Verileri güncelle (Backfill) UPDATE orders SET status = 0 WHERE status IS NULL; -- Artık NOT NULL yapılabilir (kod uyumlu hale geldikten sonra) ALTER TABLE orders ALTER COLUMN status SET NOT NULL;
4. Zero-Downtime Migration Araçları (Flyway, DbUp)
Bu stratejileri manuel olarak uygulamak zordur ve hataya açıktır. Bu nedenle, veritabanı göçlerini otomatikleştiren araçlar kullanılır.
A. DbUp (.NET Ekosistemi)
DbUp, .NET uygulamaları için en popüler veritabanı migration aracıdır. SQL script'lerini (.sql dosyaları) sırayla çalıştırır ve hangi script'lerin uygulandığını bir tabloda (SchemaVersions) tutar.
-
Avantajları: .NET ile tam entegre, basit, CI/CD pipeline'larına kolay eklenir.
-
Kullanım: Script'leri bir klasöre ekler, uygulama başlangıcında (startup) çalıştırılır veya ayrı bir konsol uygulaması olarak tetiklenir.
csharp
// DbUp ile migration'ı çalıştırma
var upgrader = DeployChanges.To
.SqlDatabase(connectionString)
.WithScriptsEmbeddedInAssembly(Assembly.GetExecutingAssembly())
.LogToConsole()
.Build();
var result = upgrader.PerformUpgrade();
B. Flyway (Java, ancak tüm platformlarla çalışır)
Flyway, daha çok Java ekosisteminde bilinse de, .NET dahil her ortamda kullanılabilir (CLI aracı olarak). Version kontrolü, basit ve güvenilirdir.
-
Avantajları: Çok popüler, çok sayıda veritabanını destekler, CI/CD ile kolay entegre olur.
-
Kullanım:
flyway migratekomutu ile çalıştırılır.
C. EF Core Migrations (Geliştirme ve Yayın için)
EF Core'un kendi migration sistemi, geliştirme aşamasında çok kullanışlıdır. Ancak, zero-downtime stratejilerini uygulamak için yeterince esnek değildir (özellikle ALTER COLUMN veya DROP COLUMN durumlarında). Bu nedenle, genellikle DbUp veya Flyway ile birlikte kullanılır.
-
Önerilen Yaklaşım:
-
Geliştirme: EF Core migrations (hızlı ve kolay).
-
Production: DbUp / Flyway (daha kontrollü ve zero-downtime odaklı). EF Core'dan çıkan SQL script'leri, DbUp ile uygulanmak üzere manuel olarak düzenlenir.
-
5. Pratik Örnek: Zero-Downtime ile Sütun Eklemek (Expand/Contract)
Senaryo: Bir Orders tablosuna total_amount adında yeni bir sütun eklemek istiyoruz. Ancak bu işlemi kesintisiz yapmalıyız.
1. Aşama (Expand): Yeni Sütunu Ekle (NULLABLE)
sql
-- Migration 1 (Expand) ALTER TABLE orders ADD COLUMN total_amount DECIMAL(18, 2) NULL;
2. Aşama (Backfill): Verileri Doldur
sql
-- Migration 2 (Data Backfill) UPDATE orders SET total_amount = subtotal + tax + shipping WHERE total_amount IS NULL;
(Bu işlem, büyük tablolarda chunk'lar halinde yapılmalıdır.)
3. Aşama (Contract): Kodu Güncelle ve Eski Sütunları Kaldır
-
Önce uygulama kodunu,
total_amountsütununu kullanacak şekilde güncelleyin. -
Eski sütunlar (ör.
subtotal,tax,shipping) hala kullanılıyorsa, kodu güncelledikten sonra eski sütunları kaldırabilirsiniz.
sql
-- Migration 3 (Contract) ALTER TABLE orders DROP COLUMN subtotal; ALTER TABLE orders DROP COLUMN tax; ALTER TABLE orders DROP COLUMN shipping; -- İsterseniz artık NOT NULL yapabilirsiniz (optional) ALTER TABLE orders ALTER COLUMN total_amount SET NOT NULL;
6. Riskler ve İpuçları
-
Büyük Tablolarda
ALTER TABLE: Büyük tablolardaALTER TABLE ADD COLUMNişlemi (özellikleNOT NULLveya varsayılan değerle) çok uzun sürebilir.ADD COLUMNişleminiNULLolarak yapmak genellikle anlıktır (metadata değişikliği). Varsayılan değer eklemek, tüm satırları günceller, bu da uzun sürer. -
Veritabanı Kilitleri (Locks): Uzun süren
ALTERveyaUPDATEişlemleri, tabloyu kilitler ve diğer işlemleri engeller. Bu nedenle, bu işlemleri düşük trafik saatlerinde veya sırayla (batch) yapın. -
Transaction ve Log Tüketimi: Büyük UPDATE işlemleri, transaction log'larını doldurabilir. İşlemi küçük parçalara bölün (chunk).
-
Sürüm Yönetimi: Migration script'lerini sürümleyin (V1, V2, ...). Her script, ya ileri (up) ya da geri (down) migration içermelidir (opsiyonel).
Sonuç:
Zero-downtime veritabanı geçişi, modern yazılım geliştirme pratiklerinin bir gereğidir. Expand/Contract deseni, backward-compatible migration'lar ve doğru araçların (DbUp, Flyway) kullanımı ile bu hedefe ulaşmak mümkündür. Unutmayın: Veritabanı geçişleri, uygulama dağıtımları ile koordine edilmelidir. Her zaman eski kodun yeni şema ile çalışabildiğinden emin olun ve kritik operasyonları düşük trafik saatlerinde planlayın. Bu stratejileri uygulayarak, canlı sistemlerinizde sıfır kesinti ile veritabanı güncellemeleri yapabilirsiniz.