Multitenancy: Single DB vs Multi DB

Birden çok müşteriye hizmet veren (SaaS) uygulamalarda tenant başına ayrı veritabanı mı, paylaşımlı veritabanı mı kullanılacağı, artıları ve eksileriyle karşılaştırılır.

Multitenancy: Single DB vs Multi DB

Multitenancy Mimarisi Tasarımı: Single DB vs. Multi DB Yaklaşımları

Multitenancy (Çok Kiracılık), tek bir uygulama instance'ının birden fazla müşteriye (tenant) hizmet vermesidir. Her tenant'ın verileri diğerlerinden izole olmalıdır. Bu izolasyonu sağlamanın üç ana yaklaşımı vardır. Bu yazıda, bu üç yaklaşımı (Database-per-Tenant, Shared Database - Separate Schema, Shared Database - Shared Schema) detaylıca karşılaştıracak, artılarını, eksilerini ve hangi senaryoda hangisinin seçilmesi gerektiğini ele alacağız.


1. Üç Temel Multitenancy Yaklaşımı

Yaklaşım Veritabanı Sunucusu Veritabanı (Database) Schema Tablo Tenant Ayırt Edici
1. Database-per-Tenant Paylaşımlı veya Ayrı Tenant başına ayrı DB Aynı veya Farklı Aynı Veritabanı adı
2. Shared DB, Separate Schema Paylaşımlı Tek DB (Shared) Tenant başına ayrı Schema Aynı Schema adı
3. Shared DB, Shared Schema Paylaşımlı Tek DB (Shared) Tek Schema (Shared) Tenant ID sütunu TenantId sütunu

Her bir yaklaşımın, maliyet, izolasyon, ölçeklenebilirlik, bakım ve geliştirme karmaşıklığı açısından farklı dengeleri vardır.


2. Yaklaşım 1: Database-per-Tenant (Tenant Başına Ayrı Veritabanı)

Her tenant'ın kendi tam teşekküllü veritabanı vardır. Bu veritabanları aynı sunucuda (farklı DB'ler) veya farklı sunucularda bulunabilir.

Artıları (Avantajları):

  • En Yüksek İzolasyon (Isolation): Tenant verileri tamamen ayrıdır. Veri sızıntısı (data leak) riski sıfıra yakındır.

  • Yedekleme ve Geri Yükleme Kolaylığı: Her tenant'ın veritabanı ayrı ayrı yedeklenebilir ve geri yüklenebilir. Bir tenant'ın verisi bozulursa, diğerleri etkilenmez.

  • Performans İzolasyonu: Bir tenant'ın yoğun sorguları diğer tenant'ları etkilemez (farklı sunuculardalarsa).

  • Özelleştirme (Customization): Büyük tenant'lar için özel index'ler, trigger'lar veya farklı veritabanı sürümleri kullanılabilir.

  • Veri Taşıma (Migration) Kolaylığı: Bir tenant'ı başka bir sunucuya taşımak çok kolaydır.

Eksileri (Dezavantajları):

  • Yüksek Maliyet: Her tenant için ayrı bir veritabanı, özellikle çok sayıda küçük tenant varsa, lisans ve sunucu maliyetlerini katlar.

  • Yönetim Zorluğu: Onlarca veya yüzlerce tenant varsa, veritabanı yönetimi (backup, restore, migration, monitoring) karmaşıklaşır. Otomasyon şarttır.

  • Schema Migration Zorluğu: Uygulama güncellendiğinde, tüm tenant veritabanlarına schema değişikliği (migration) uygulanmalıdır. Dağıtık bir işlemdir (örn. Flyway, DbUp ile otomatize edilebilir).

  • Connection Pool Yönetimi: Çok sayıda farklı veritabanı bağlantısı, connection pool'u zorlayabilir.

Ne Zaman Kullanılır?

  • Kurumsal (Enterprise) SaaS: Büyük müşteriler (ör. bankalar, büyük firmalar) veri izolasyonunu ve özel yedekleme politikalarını talep eder.

  • Veri Güvenliği Düzenlemeleri (GDPR, HIPAA): Müşteri verilerinin tamamen ayrı tutulması gereken sektörler (sağlık, finans).

  • Tenant Sayısı Az ve Her Biri Büyük: 50-100 büyük tenant, her biri kendi veritabanını hak eder.


3. Yaklaşım 2: Shared Database, Separate Schema

Tüm tenant'lar aynı veritabanını (ör. aynı SQL Server instance'ını) paylaşır, ancak her tenant'ın kendi schema'sı (ör. tenant1, tenant2) vardır. Tablolar (ör. Orders, Products) her schema içinde aynı isimle bulunur.

Artıları (Avantajları):

  • Daha Düşük Maliyet: Tek bir veritabanı sunucusu, maliyetleri düşürür (özellikle lisans maliyetleri).

  • İyi İzolasyon: Schema'lar birbirinden izoledir. Veri sızıntısı riski düşüktür.

  • Yönetim Kolaylığı: Tek bir sunucu yönetmek, 100 sunucu yönetmekten daha kolaydır.

  • Yedekleme Tek Bir Noktadan: Tüm veritabanı tek seferde yedeklenir.

Eksileri (Dezavantajları):

  • Veritabanı Sınırlamaları: Bazı veritabanları (ör. MySQL) schema kavramını tam desteklemez. (PostgreSQL, SQL Server, Oracle iyi destekler).

  • Performans Etkisi: Kötü yazılmış sorgular veya yoğun tenant, diğer tenant'ları etkileyebilir (kaynak paylaşımı).

  • Migration Zorluğu: Her tenant'ın schema'sına ayrı ayrı migration uygulamak gerekir (ancak bunu otomatize etmek mümkündür).

  • Veri Taşıma Zorluğu: Bir tenant'ı başka bir sunucuya taşımak, tüm schema'yı kopyalamayı gerektirir (daha zordur).

Ne Zaman Kullanılır?

  • Orta Ölçekli SaaS: Yüzlerce ila binlerce tenant, her biri orta büyüklükte veriye sahip.

  • PostgreSQL / SQL Server Kullanılıyorsa: Bu veritabanları schema izolasyonunu çok iyi destekler.

  • İzolasyon Talebi Olan Ancak Maliyet Duyarlılığı Olan Müşteriler.


4. Yaklaşım 3: Shared Database, Shared Schema (Tenant ID ile)

Tüm tenant'lar aynı veritabanını ve aynı schema'yı paylaşır. Her tabloya, hangi tenant'a ait olduğunu belirten bir TenantId sütunu eklenir. Tüm sorgularda bu sütun filtrelenir.

Artıları (Avantajları):

  • En Düşük Maliyet: Tek veritabanı, tek schema, en az kaynak tüketimi.

  • En Kolay Yönetim: Tek bir veritabanı yedeklemek, restore etmek, migration uygulamak çok basittir.

  • Kolay Ölçeklendirme: Okuma replikaları, sharding gibi tekniklerle kolayca ölçeklendirilebilir.

  • EF Core ile Kolay Entegrasyon: Global Query Filter ile TenantId otomatik filtrelenebilir.

Eksileri (Dezavantajları):

  • En Düşük İzolasyon: Veri sızıntısı riski en yüksektir. TenantId filtresi unutulursa, diğer tenant'ların verileri görünebilir.

  • Performans Riskleri: Bir tenant'ın ağır sorguları, tüm veritabanını etkiler. Index'ler dikkatli tasarlanmalıdır.

  • Veri Kurtarma Zorluğu: Bir tenant'ın verilerini geri yüklemek (örn. yanlışlıkla silme) zordur. Tüm veritabanını geri almak gerekir.

  • Büyük Veri: Veritabanı çok büyüdüğünde (milyarlarca satır), sharding veya arşivleme gerekir.

Ne Zaman Kullanılır?

  • Başlangıç Aşamasındaki veya Küçük Ölçekli SaaS: Prototipler, MVP'ler, düşük maliyetli hizmetler.

  • Tenant Sayısı Çok Fazla (Onbinler) ve Her Biri Çok Küçük Veriye Sahip: Örneğin, bir blog platformu veya not alma uygulaması.

  • Maliyet Hassasiyeti Yüksek Olan Projeler.


5. Karşılaştırma Tablosu

Kriter Database-per-Tenant Shared DB, Separate Schema Shared DB, Shared Schema
Veri İzolasyonu 🟢 Çok Yüksek 🟡 Yüksek 🔴 Düşük
Maliyet 🔴 Yüksek 🟡 Orta 🟢 Düşük
Yönetim Karmaşıklığı 🔴 Yüksek 🟡 Orta 🟢 Düşük
Yedekleme/Geri Yükleme 🟢 Kolay (Tenant bazlı) 🟡 Orta (Schema bazlı) 🔴 Zor (Tüm DB)
Migration 🔴 Zor (Tüm DB'ler) 🟡 Orta (Tüm Schema'lar) 🟢 Kolay (Tek DB)
Performans İzolasyonu 🟢 Yüksek 🟡 Orta 🔴 Düşük
Veri Taşıma (Rebalancing) 🟢 Kolay 🟡 Orta 🔴 Zor
Ölçeklenebilirlik (Scale-out) 🟢 Kolay (Sharding) 🟡 Orta 🟢 Kolay (Read Replica)
SaaS Büyüklüğü İçin Uygun Kurumsal, Büyük Orta Ölçekli Küçük, Başlangıç

6. .NET Core / EF Core ile Multitenancy Uygulama İpuçları

A. Tenant Belirleme (Tenant Resolution):
Tenant'ı genellikle subdomain (tenant1.yourapp.com), URL path (yourapp.com/tenant1) veya JWT token'dan (tenantId claim) belirlersiniz.

csharp

public class TenantService
{
    private readonly IHttpContextAccessor _httpContextAccessor;

    public TenantService(IHttpContextAccessor httpContextAccessor)
    {
        _httpContextAccessor = httpContextAccessor;
    }

    public string GetCurrentTenantId()
    {
        // Subdomain'den veya claim'den al
        return _httpContextAccessor.HttpContext?.User?.FindFirst("tenantId")?.Value 
               ?? "default";
    }
}

B. DbContext'in Dinamik Yapılandırılması (Database-per-Tenant için):
Her tenant için farklı connection string kullanmak için OnConfiguring metodunu override edin.

csharp

public class AppDbContext : DbContext
{
    private readonly TenantService _tenantService;
    private readonly IConfiguration _configuration;

    public AppDbContext(TenantService tenantService, IConfiguration configuration)
    {
        _tenantService = tenantService;
        _configuration = configuration;
    }

    protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder)
    {
        var tenantId = _tenantService.GetCurrentTenantId();
        var connectionString = _configuration.GetConnectionString($"TenantDb_{tenantId}");
        optionsBuilder.UseSqlServer(connectionString);
    }
}

C. Global Query Filter ile Tenant ID Otomatik Filtreleme (Shared DB, Shared Schema için):
EF Core'un Global Query Filter özelliği ile tüm sorgulara otomatik olarak TenantId filtresi ekleyebilirsiniz.

csharp

public class AppDbContext : DbContext
{
    private readonly TenantService _tenantService;

    public AppDbContext(DbContextOptions<AppDbContext> options, TenantService tenantService)
        : base(options)
    {
        _tenantService = tenantService;
    }

    protected override void OnModelCreating(ModelBuilder modelBuilder)
    {
        modelBuilder.Entity<Order>().HasQueryFilter(e => e.TenantId == _tenantService.GetCurrentTenantId());
        modelBuilder.Entity<Product>().HasQueryFilter(e => e.TenantId == _tenantService.GetCurrentTenantId());
        // Tüm entity'ler için tekrarla...
    }
}

D. Tenant ID'yi Otomatik Ekleme (Shadow Property):
TenantId'yi her entity'de manuel olarak tanımlamak yerine, EF Core'un shadow property özelliğini kullanabilirsiniz.

csharp

// OnModelCreating içinde:
modelBuilder.Entity<Order>().Property<string>("TenantId");
modelBuilder.Entity<Product>().Property<string>("TenantId");

// SaveChanges öncesinde TenantId'yi ata:
public override async Task<int> SaveChangesAsync(CancellationToken cancellationToken = default)
{
    var entries = ChangeTracker.Entries()
        .Where(e => e.State == EntityState.Added || e.State == EntityState.Modified)
        .Where(e => e.Metadata.FindProperty("TenantId") != null);

    foreach (var entry in entries)
    {
        entry.Property("TenantId").CurrentValue = _tenantService.GetCurrentTenantId();
    }

    return await base.SaveChangesAsync(cancellationToken);
}

7. Hangi Yaklaşımı Seçmelisiniz? (Karar Ağacı)

  1. Veri güvenliği düzenlemeleri (GDPR, HIPAA) veya müşteri talepleri izolasyon gerektiriyor mu?

    • Evet → Database-per-Tenant (En güvenli).

    • Hayır → Devam et.

  2. Toplam tenant sayısı ve veri büyüklüğü nedir?

    • Az tenant (<100), her biri çok büyük veri → Database-per-Tenant (Performans ve yönetim).

    • Çok tenant (>1000), her biri küçük veri → Shared DB, Shared Schema (Maliyet etkin).

    • Orta (100-1000) → Shared DB, Separate Schema (Denge).

  3. Ekip tecrübesi ve operasyonel kapasite nedir?

    • Otomasyon (CI/CD, migration, backup) güçlüyse → Database-per-Tenant veya Separate Schema tercih edilebilir.

    • Küçük ekip, kaynak kısıtlı → Shared DB, Shared Schema daha pratiktir.

Sonuç:

Multitenancy tasarımı, güvenlik, maliyet, yönetim ve ölçeklenebilirlik arasında bir denge kurmaktır. Database-per-Tenant, en yüksek izolasyonu sağlar ancak en pahalı ve yönetimi en zor olanıdır. Shared DB, Shared Schema en ucuz ve en kolayıdır ancak izolasyon zafiyeti ve performans riskleri taşır. Shared DB, Separate Schema ise bu ikisi arasında iyi bir denge sunar.

Kesin bir kural yoktur. Projenizin büyüklüğüne, bütçesine, güvenlik gereksinimlerine ve ekip kapasitesine göre karar vermelisiniz. Birçok başarılı SaaS şirketi, başlangıçta Shared DB, Shared Schema ile başlayıp, büyük müşterileri Database-per-Tenant'a taşımaktadır (hybrid yaklaşım). Bu esneklik, uzun vadeli başarının anahtarıdır.

Tüm yazılar