Database Engineering

Boşlukları Kapatmak: Parçalanmış PostgreSQL Kümelelerinde Sıfır Kesintiyle Şema Değişikliklerini Uygulamak

Veritabanı şema değişikliklerini yönetmek genellikle mühendislik ekipleri için bir sürtünme kaynağıdır ve genellikle hizmet kullanılabilirliğini kesintiye uğratan bakım pencereleri gerektirir. Ancak, modern dağıtık mimarilerde bu sürtünme kabul edilemez. Verileriniz birden fazla PostgreSQL örneği arasında parçalandığında (sharded), bu zorluk önemli ölçüde artar. Tek bir şema değişikliği yalnızca tek bir düğümdeki bir komut değil; üretim verilerinizin bir dilimini tutan onlarca parça arasında koordineli bir dans gibidir.

Bu makale, parçalanmış PostgreSQL ortamlarında çevrimiçi şema değişikliklerini uygulamak için gereken mimari desenleri ve operasyonel stratejileri incelemektedir. Geleneksel kilitleme mekanizmalarının sınırlarının ötesine geçerek, mantıki kopyalamanın (logical replication), çift yazma (dual-write) desenlerinin ve artımlı göçün (incremental migration) hizmetlerinizi %100 süreyle çalışır durumda tutmasını derinlemesine ele alacağız.

Parçalama Kısıtlaması

Göçü uygulamadan önce, parçalanmış bir topolojinin kısıtlamalarını anlamak gerekir. Tek bir monolitik veritabanında ALTER TABLE komutunu dışlayıcı bir kilit ile çalıştırmak kadar basit olanın aksine, parçalama veri tutarlılığı ve koordinasyon konusunda karmaşıklık getirir.

Parçalanmış bir kurulumda, uygulama sorguları bir parçalama anahtarına (örneğin user_id) göre belirli parçalara yönlendirir. Bir şema değişikliği bir parçada yapılırken diğerlerinde yapılmazsa veya değişiklik uzun bir kilit oluşumuna neden olacak şekilde gerçekleştirilirse, uygulama kısmi başarısızlıklar, tutarsız veri görünürlüğü veya pencere sırasında tam kesinti yaşayabilir. Bu nedenle, göç stratejisi idempotent, güvenli olmalı ve standart okuma/yazma trafiğini engelleyen kilitler tutmaksızın çalıştırılabilir olmalıdır.

Çift Yazma Stratejisi

Sıfır kesintili şema değişiklikleri için en sağlam desen, genellikle çift yazmaya odaklanan aşamalı bir yaklaşımdır. Bu yöntem, mevcut uygulama mantığını hemen bozmadan yeni bir sütun tanımanıza olanak tanır.

Aşama 1: Yeni Sütunu Geri Doldur (Backfill)

Öncelikle, tüm parçalarda şemaya yeni sütunu ekleyin. Varsayılan değer olmadan ADD COLUMN kullanmak son derece hızlıdır çünkü PostgreSQL yalnızca tuple başlığını günceller, mevcut veriyi değil.

-- Tüm parçalarda çalıştırın
ALTER TABLE events ADD COLUMN new_tracking_id UUID;

Bu aşamada uygulama normal şekilde çalışmaya devam eder. Eski ve yeni veriler birlikte var olur. Ancak, yeni sütun şu anda tüm tarihi kayıtlar için NULL'dır.

Aşama 2: Geri Doldurma ve Çift Yazma

Sonraki adım, yeni sütunu tarihi verilerle doldurmak için bir arka plan işi çalıştırmaktır. Bu işlem, I/O alt sistemini aşırı yüklememek için hız sınırlı (throttled) olmalıdır. Eş zamanlı olarak, uygulama kodunu hem eski hem de yeni sütuna yazacak şekilde güncelleyin (çift yazma).

-- Uygulama Mantığı Pseudo-kodu
INSERT INTO events (event_type, old_tracking_id, new_tracking_id)
VALUES ('purchase', 12345, gen_random_uuid());

Her iki sütuna yazarak, uygulama mantığı geçici olarak eski sütuna geri dönerse bile, verinin yeni yapıda korunduğunu garanti etmiş olursunuz. Bu aşama, tüm parçalar arasında veri bütünlüğünü sağlamak için ne kadar gerekliyse o kadar uzun sürebilir.

Koordinasyon için Mantıki Kopyalama

Karmaşık şema değişiklikleri için veya alt yapıyı tamamen değiştirmeniz gerektiğinde, mantıki kopyalama güçlü bir alternatif sunar. Birincil düğümde bir mantıki kopyalama yuvası oluşturabilir ve değişiklikleri replikalardaki bir "gölge" tabloya veya ayrı bir şemaya akış halinde iletebilirsiniz.

Bu yaklaşım, veri türlerini değiştirmeniz veya tabloları önemli ölçüde yeniden yapılandırmanız gerektiğinde özellikle yararlıdır. Süreç şunları içerir:

  1. İstenen şemaya sahip bir replika tablosu oluşturma.
  2. Mantıki kopyalama kullanarak birincilden replika tablosuna değişiklikleri akış halinde iletme.
  3. İlk veri kümesini senkronize etmek için bir geri doldurma (backfill) işi çalıştırma.
  4. Veri tamamen tutarlı olduğunda uygulama okumalarını yeni tabloya yönlendirme.

Mantıki kopyalama asenkron olduğu için, birincil düğümdeki yazmaları engellemez ve senkronizasyon süreci boyunca sürekli kullanılabilirliği sağlar.

Değişikliğin Yayılması

Çift yazma aşaması tamamlandıktan ve geri doldurma işlemi bittikten sonra, nihai geçiş için hazırsınız. Bu aşama genellikle en hızlısıdır çünkü veri zaten yerindedir.

  1. Sadece Okuma Geçişi: Uygulamayı yalnızca yeni sütundan veya yeni tablo yapısından okuyacak şekilde güncelleyin.
  2. Eski Sütunu Sil: Eski yapıdan veri okunmadığından emin olduktan sonra, eski sütunu kaldırın.
  3. Kısıtlamalar Ekle: Son olarak, veri bütünlüğünü garanti etmek için yeni sütuna NOT NULL kısıtlamaları veya benzersiz indeksler uygulayın.
-- Aşama 3: Temizleme
ALTER TABLE events DROP COLUMN old_tracking_id;
ALTER TABLE events ALTER COLUMN new_tracking_id SET NOT NULL;

Sonuç

Parçalanmış PostgreSQL kümelelerinde şema değişikliklerini yürütmek, "kilitle ve sil" zihniyetinden uzaklaşmayı gerektirir. Çift yazma desenlerini, arka plan geri doldurma işlemlerini ve mantıki kopyalamayı kullanarak mühendisler gerçek sıfır kesintili göçler elde edebilirler. Bu stratejiler, uygulama kodunda ve operasyonel araçlarda başlangıçta karmaşıklık getirse de, ödül, kullanılabilirlikten ödün vermeden işinizle birlikte ölçeklenen bir veritabanı altyapısıdır.

Unutmayın, amaç sadece şemayı değiştirmek değil, bunu son kullanıcı için şeffaf bir şekilde yapmaktır. Dikkatli planlama ve iteratif dağıtım ile parçalanmış mimariniz, ürün gereksinimlerinizin talep ettiği hızda evrilebilir.

Share: