Master-Slave Mimari: Tek Veritabanından Ölçeklenebilir ve Dayanıklı Sisteme

Bir sistem büyüdükçe en kritik soru şuna dönüşür: Veritabanı çökerse ne olur? Bu yazıda tek veritabanlı bir mimarinin neden riskli olduğunu, Master-Slave replikasyonun bu riski nasıl azalttığını, okuma ölçeklendirmesini nasıl sağladığını ve işin Master-Master, split-brain, consensus, sharding gibi daha ileri konulara nasıl bağlandığını anlatacağız.

Bir sistem büyüdükçe en kritik soru şuna dönüşür: Veritabanı çökerse ne olur? Bu yazıda tek veritabanlı bir mimarinin neden riskli olduğunu, Master-Slave replikasyonun bu riski nasıl azalttığını, okuma ölçeklendirmesini nasıl sağladığını ve işin Master-Master, split-brain, consensus, sharding gibi daha ileri konulara nasıl bağlandığını anlatacağız.

Tek Veritabanı Neden Risklidir?

Şimdi şöyle bir düşün: Mobil uygulamadan gelen istekler önce bir load balancer üzerinden uygulama sunucularına dağıtılıyor. Sunucuların hepsi aynı veritabanına gidiyorsa, uygulama tarafında kaç sunucun olduğu bir noktadan sonra çok da önemli değildir. Çünkü veri katmanında hâlâ tek bir kritik düğüm vardır.

Single Point of Failure, yani şöyle düşün: Sistemde tek başına bozulduğunda tüm yapıyı durdurabilecek bileşendir. Tek veritabanı da bunun en klasik örneklerinden biridir.

flowchart TD
    U1["Mobil Kullanıcı 1"] --> LB["Load Balancer"]
    U2["Mobil Kullanıcı 2"] --> LB
    U3["Mobil Kullanıcı 3"] --> LB

    LB --> S1["Uygulama Sunucusu 1"]
    LB --> S2["Uygulama Sunucusu 2"]
    LB --> S3["Uygulama Sunucusu 3"]

    S1 --> DB[("Tek Veritabanı")]
    S2 --> DB
    S3 --> DB

    DB -. "Çökerse sistem durur" .-> FAIL["Servis Kesintisi"]

Bu diyagramda uygulama sunucuları çoğaltılmış olsa da veritabanı tek kalıyor. Bu yüzden veritabanı tarafındaki arıza tüm uygulamayı etkileyebilir.

Replikasyon: Verinin Kopyasını Tutmak

Bu problemi azaltmanın en temel yolu verinin başka bir yerde kopyasını tutmaktır. Replication, yani şöyle düşün: Ana veritabanındaki değişikliklerin başka bir veritabanına aktarılmasıdır.

Buradaki amaç sadece “yedek almak” değildir. Replika veritabanı sayesinde hem arıza anında toparlanmak kolaylaşır hem de bazı okuma işlemleri ana veritabanından alınarak yük dağıtılabilir.

flowchart LR
    MASTER[("Master Veritabanı")] -->|"Veri değişiklikleri aktarılır"| SLAVE[("Slave Replika")]
    APP["Uygulama Sunucuları"] -->|"Yazma işlemleri"| MASTER
    APP -->|"Okuma işlemleri"| SLAVE

Bu yapı Master-Slave mimarinin temelidir. Master yazma işlemlerini kabul eder, Slave ise Master’dan gelen değişiklikleri takip ederek verinin kopyasını taşır.

Senkron ve Asenkron Replikasyon

Replikasyon iki temel şekilde yapılabilir: senkron veya asenkron.

Asynchronous replication, yani şöyle düşün: Master veriyi günceller, sonra uygun bir zamanda bu değişikliği Slave’e gönderir. Bu hızlıdır ama kısa süreli veri farkları oluşabilir.

Synchronous replication, yani şöyle düşün: Master bir değişikliği tamamlanmış saymadan önce Slave tarafına da ulaştığından emin olur. Bu daha tutarlıdır ama yazma işlemini yavaşlatabilir.

sequenceDiagram
    participant App as Uygulama
    participant M as Master DB
    participant S as Slave DB

    App->>M: Yazma isteği
    M->>S: Değişikliği gönder
    alt Senkron replikasyon
        S-->>M: Onay
        M-->>App: İşlem başarılı
    else Asenkron replikasyon
        M-->>App: İşlem başarılı
        M->>S: Değişiklik daha sonra aktarılır
    end

Bu diyagram iki yaklaşım arasındaki farkı gösteriyor. Senkron modelde tutarlılık öne çıkar, asenkron modelde ise hız ve düşük gecikme daha önemlidir.

Master-Slave Mimarisinde Roller

Master, yazma işlemlerinin merkezi otoritesidir. Bunu şöyle hayal edebilirsin: Sistemde verinin resmi halini belirleyen düğüm Master’dır.

Slave ise Master’dan gelen değişiklikleri uygular. Genellikle doğrudan yazma kabul etmez. Bu kural çok önemlidir, çünkü Slave’e bağımsız yazma izni verirsen artık tek yönlü Master-Slave ilişkisi bozulur.

flowchart TD
    W["Yazma İsteği"] --> M[("Master")]
    M -->|"Transaction log / değişiklik kaydı"| R[("Slave")]
    Q["Okuma İsteği"] --> R
    Q2["Kritik Okuma"] --> M

    BAD["Slave'e yazma isteği"] -. "Reddedilir" .-> R

Bu yapıda yazma işlemleri Master’a gider. Slave daha çok okuma yükünü taşır ve Master’ın değişikliklerini takip eder.

Slave Yazma Kabul Ederse Ne Olur?

Eğer Slave de yazma kabul etmeye başlarsa sistemin doğası değişir. Artık iki taraf da verinin sahibi gibi davranır.

Master-Master architecture, yani şöyle düşün: Birden fazla veritabanı düğümü yazma işlemi kabul eder ve kendi değişikliklerini diğer düğümlere yaymaya çalışır.

Bu ilk bakışta daha dayanıklı görünür. Fakat dağıtık sistemlerde asıl zorluk tam burada başlar: İki düğüm aynı anda farklı kararlar alırsa hangisi doğru kabul edilecek?

flowchart LR
    A[("Master A")] <-->|"Değişiklikleri eşitle"| B[("Master B")]

    C1["Kullanıcı işlemi 1"] --> A
    C2["Kullanıcı işlemi 2"] --> B

    A -. "A kendi durumunu doğru sanır" .-> PA["Durum A"]
    B -. "B kendi durumunu doğru sanır" .-> PB["Durum B"]

Bu diyagram Master-Master ilişkisini gösteriyor. İki taraf da yazma kabul ettiği için veri tutarlılığı artık daha karmaşık bir probleme dönüşür.

Split-Brain Problemi

Split-brain, yani şöyle düşün: Sistemdeki iki düğüm birbirini göremediği için ikisi de “asıl karar verici benim” diye davranır.

Bu genellikle ağ bölünmesiyle olur. Düğümler çalışıyordur ama aralarındaki bağlantı kopmuştur. Sonuçta her iki taraf da yazma kabul eder ve sistem farklı gerçekliklere ayrılır.

flowchart TD
    A[("Master A")] -. "Bağlantı koptu" .- X["Ağ Problemi"]
    X -. "Bağlantı koptu" .- B[("Master B")]

    U1["İşlem A"] --> A
    U2["İşlem B"] --> B

    A --> SA["Durum: X"]
    B --> SB["Durum: Y"]

    SA -. "Çakışma" .-> CONFLICT["Tutarsız Veri"]
    SB -. "Çakışma" .-> CONFLICT

Split-brain problemi, dağıtık veritabanı sistemlerinin en tehlikeli senaryolarından biridir. Çünkü sistem tamamen çökmüş gibi görünmez; çalışıyor gibi görünür ama yanlış veri üretebilir.

Üçüncü Düğüm ve Consensus Fikri

Bu noktada iş distributed consensus konusuna yaklaşır. Distributed consensus, yani şöyle düşün: Birden fazla düğümün aynı değer veya durum üzerinde anlaşmasıdır.

Üçüncü bir düğüm eklemek, kararları çoğunluk mantığına yaklaştırır. Böylece iki düğüm arasındaki bağlantı koptuğunda sistem hangi tarafın güncel kabul edileceğini daha kontrollü belirleyebilir.

sequenceDiagram
    participant A as Düğüm A
    participant B as Düğüm B
    participant C as Düğüm C

    A->>C: Yeni durumu önerir
    C-->>A: Durum kabul edildi
    B->>C: Eski duruma dayalı başka güncelleme önerir
    C-->>B: Önce güncel durumu al
    B->>B: İşlemi geri al veya yeniden dene

Bu diyagram üçüncü düğümün hakem gibi davranmasını basitleştirilmiş şekilde gösteriyor. Gerçek sistemlerde bu mantık 2PC, 3PC, Raft, Paxos gibi protokollerle daha formal hale getirilir.

2PC, 3PC, MVCC ve Saga Nerede Devreye Girer?

Two-Phase Commit, yani şöyle düşün: Bir işlem önce tüm katılımcılara “hazır mısınız?” diye sorar, sonra herkes hazırsa commit eder. Güçlü tutarlılık sağlar ama yavaş ve bloklayıcı olabilir.

Three-Phase Commit, yani şöyle düşün: 2PC’nin bazı tıkanma risklerini azaltmak için araya ek bir aşama koyan yaklaşımdır.

MVCC, yani şöyle düşün: Verinin tek bir kopyası yerine farklı zamanlardaki sürümlerini tutarak okuma ve yazma işlemlerinin birbirini daha az engellemesini sağlar. PostgreSQL gibi sistemlerde bu yaklaşım önemli bir rol oynar.

Saga, yani şöyle düşün: Uzun süren bir iş sürecini küçük yerel işlemlere böler ve bir adım başarısız olursa telafi edici işlemlerle geri toparlar. Mikroservis mimarilerinde ödeme, rezervasyon, stok ayırma gibi süreçlerde sıkça karşımıza çıkar.

flowchart TD
    START((Başlangıç)) --> T1["Yerel İşlem 1"]
    T1 --> T2["Yerel İşlem 2"]
    T2 --> T3["Yerel İşlem 3"]

    T3 --> OK{Başarılı mı?}
    OK -->|"Evet"| DONE((Tamamlandı))
    OK -->|"Hayır"| C3["Telafi 3"]
    C3 --> C2["Telafi 2"]
    C2 --> C1["Telafi 1"]
    C1 --> FAIL((Geri Alındı))

Bu diyagram Saga mantığını gösteriyor. Büyük bir dağıtık işlem tek parça halinde kilitlenmek yerine küçük adımlara ayrılıyor.

Master-Slave Mimari Neden Kullanılır?

Master-Slave mimarinin en büyük avantajlarından biri okuma ölçeklendirmesidir. Yazma işlemleri Master’a giderken, raporlama, listeleme, analiz veya daha az kritik okuma işlemleri Slave düğümlerden yapılabilir.

Bunu şöyle hayal edebilirsin: Master sistemin karar defteri gibi çalışır, Slave’ler ise bu defterin kopyalarını okuyuculara sunar.

flowchart LR
    APP["Uygulama"] -->|"Yazma"| M[("Master")]
    M --> S1[("Slave 1")]
    M --> S2[("Slave 2")]
    M --> S3[("Slave 3")]

    R1["Okuma Trafiği"] --> S1
    R2["Raporlama"] --> S2
    R3["Analitik İşler"] --> S3

Bu yapı özellikle okuma trafiği yazma trafiğinden çok fazla olan sistemlerde işe yarar. Sosyal ağ akışları, içerik platformları, ürün katalogları ve raporlama ekranları buna iyi örneklerdir.

Sharding ile Hasarı Küçültmek

Sharding, yani şöyle düşün: Tüm veriyi tek veritabanında tutmak yerine belirli kurallara göre parçalara bölmektir. Örneğin kullanıcıları ID aralığına, ülkeye, kuruma veya başka bir anahtara göre farklı veritabanlarına dağıtabilirsin.

Sharding tek başına yedeklilik değildir. Ama her shard için bir replika eklersen hem yükü bölersin hem de arıza alanını daraltırsın.

flowchart TD
    ROUTER["Shard Router"] --> A[("Shard A\nKullanıcı 0-99")]
    ROUTER --> B[("Shard B\nKullanıcı 100-199")]
    ROUTER --> C[("Shard C\nKullanıcı 200-299")]

    A --> AR[("Shard A Replica")]
    B --> BR[("Shard B Replica")]
    C --> CR[("Shard C Replica")]

    C -. "Çökerse" .-> CR
    CR -. "Yeni Master olabilir" .-> ROUTER

Bu diyagram shard edilmiş bir yapıda her parçanın kendi replikasına sahip olduğunu gösteriyor. Bir shard çöktüğünde tüm sistem değil, sadece ilgili veri aralığı etkilenir.

Özet

Master-Slave mimari, tek veritabanının oluşturduğu riski azaltmak ve okuma trafiğini ölçeklemek için kullanılan temel yaklaşımlardan biridir. Master yazma işlemlerini yönetirken Slave düğümler verinin kopyasını taşır ve okuma yükünü paylaşabilir. Fakat sistem büyüdükçe konu yalnızca kopya tutmaktan ibaret kalmaz; senkron/asenkron replikasyon, split-brain, consensus, MVCC, Saga, 2PC, 3PC ve sharding gibi kavramlar devreye girer. Asıl mesele, hız, tutarlılık ve dayanıklılık arasında bilinçli bir denge kurmaktır.

Bu yazı Distributed Consensus and Data Replication strategies on the server videosundan ilham alınarak yazılmıştır.


Kaynakça

Leave a Reply

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