Çok kiracılı (multi-tenant) mimari, bir uygulamanın tek kurulumla birden fazla müşteriye hizmet vermesidir. Fintech platformu, kurumsal yönetim sistemi ya da ekip uygulaması kuruyor olun, kiracıları nasıl ayırdığınız maliyeti, güvenliği ve büyümeyi birlikte belirler. Bu yazı üç temel kalıbı, aralarındaki ödünleşimleri ve doğru seçimi nasıl yapacağınızı anlatıyor.
Çok kiracılık nedir
Çok kiracılı bir sistemde tek bir uygulama birçok müşteriye (kiracıya) hizmet verir ve her kiracının verisi diğerlerinden ayrı tutulur. Her müşteriye ayrı altyapı kurulan tek kiracılı modelden farkı, kaynakların paylaşılması ama verinin mantıksal olarak ayrılmasıdır.
Kazancı açık: daha az işletme yükü, müşteri başına daha düşük altyapı maliyeti, tek dağıtım hattı ve yeni özelliğin herkese aynı anda ulaşması. Bedeli de açık: veri izolasyonu, güvenlik ve kiracıya özel ayarlar daha dikkatli tasarım ister.
Üç temel kalıp
1. Kiracı başına veritabanı
Her kiracıya ayrı bir veritabanı verilir. İzolasyonu sağlamanın en anlaşılır yoludur ve uyum gereksinimlerinin sıkı olduğu sektörlerde (finans, sağlık) sık tercih edilir.
Artıları:
- En yüksek veri izolasyonu
- Kiracıya özel yedekleme ve geri yükleme
- Kiracı bazında denetim kolaylığı
- Bir kiracının verisini taşımak ya da silmek basit
Eksileri:
- Çok sayıda veritabanını işletmek zahmetli
- Altyapı maliyeti yüksek
- Kiracılar arası raporlama zor
- Şema değişikliklerini yüzlerce veritabanına uygulamak ayrı bir iş
2. Ortak veritabanı, kiracı başına şema
Tek bir veritabanı sunucusu, her kiracı için ayrı şema. İstek, kiracı kimliğine göre doğru şemaya yönlendirilir:
CREATE SCHEMA tenant_acme;
CREATE SCHEMA tenant_globex;
CREATE TABLE tenant_acme.users (
id uuid PRIMARY KEY,
email text NOT NULL
);
-- İstek başında doğru şemayı seçmek
SET search_path TO tenant_acme;
Artıları:
- Kiracı başına veritabanından daha verimli kaynak kullanımı
- İşletmesi daha kolay
- İzolasyon ile maliyet arasında iyi bir denge
Eksileri:
- Sorgu yönlendirmesinde hata payı
- Kiracı sayısı arttıkça migration’lar uzar
- Gürültülü komşu (noisy neighbor) etkisi
- Bağlantı havuzu ile
search_path’in birlikte yönetilmesi dikkat ister
3. Ortak tablolar, satır düzeyinde izolasyon
En maliyet etkin kalıp: tüm kiracıların verisi aynı tablolarda, her satırda bir tenant_id sütunuyla. Erişim uygulama mantığıyla ve tercihen veritabanı politikalarıyla denetlenir.
CREATE TABLE projects (
id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
owner_id uuid NOT NULL,
name text NOT NULL,
created_at timestamptz NOT NULL DEFAULT now()
);
CREATE INDEX projects_tenant_idx ON projects (tenant_id);
Artıları:
- En düşük altyapı maliyeti
- En kolay işletme
- Kiracılar arası analitik basit
- Çok sayıda küçük kiracıya en uygun kalıp
Eksileri:
- İzolasyon tamamen doğru filtrelemeye bağlı
- Unutulan tek bir
WHERE tenant_id = ...veri sızıntısı demek - Tek bir kiracıyı yedeklemek ya da geri yüklemek zor
Bu riski azaltmanın en etkili yolu filtreyi uygulamaya bırakmamak, PostgreSQL’in satır düzeyi güvenliğiyle (RLS) veritabanına da yazmaktır:
ALTER TABLE projects ENABLE ROW LEVEL SECURITY;
ALTER TABLE projects FORCE ROW LEVEL SECURITY;
CREATE POLICY tenant_isolation ON projects
USING (tenant_id = current_setting('app.tenant_id')::uuid);
Uygulama her işlemin başında kiracıyı oturuma yazar; filtreyi unutan bir sorgu bile başka kiracının satırını göremez:
// Kiracı bağlamı her transaction'ın başında set edilir
async function withTenant(pool, tenantId, work) {
const client = await pool.connect();
try {
await client.query('BEGIN');
await client.query("SELECT set_config('app.tenant_id', $1, true)", [tenantId]);
const result = await work(client);
await client.query('COMMIT');
return result;
} catch (err) {
await client.query('ROLLBACK');
throw err;
} finally {
client.release();
}
}
set_config(..., true) ayarı yalnız o transaction için geçerli kılar; bağlantı havuza döndüğünde bir sonraki istek önceki kiracının bağlamını devralmaz.
Kiracıyı tanımak
Hangi kalıbı seçerseniz seçin, her isteğin hangi kiracıya ait olduğunu güvenilir biçimde bilmeniz gerekir:
- Alt alan adı:
acme.uygulama.com - Yol:
uygulama.com/acme - Kimlik belirteci (JWT) içeriği: kiracı kimliği token’da, imzayla korunur
- Başlık:
X-Tenant-IDgibi; tek başına güvenilmez, istemci istediğini yazabilir
Kiracı kimliği istemcinin gönderdiği bir değerden değil, doğrulanmış kimlikten türetilmelidir. Alt alan adı ile token’daki kiracı birbirini tutmuyorsa istek reddedilir.
Uygulamada dikkat edilecekler
Güvenlik ve izolasyon
- Kiracı bağlamını her veri erişim noktasında doğrulayın
- Ortak tablolarda PostgreSQL RLS kullanın
- Kiracı verisine erişimi denetim kaydına yazın
- İzolasyonu test edin: A kiracısının oturumuyla B’nin kaydını isteyen otomatik testler
- Önbellek anahtarlarına kiracıyı ekleyin (
tenant:{id}:...); aksi hâlde önbellek sızıntı kaynağı olur
Ölçekleme
- Parçalama (sharding): kiracıları birden fazla veritabanına dağıtmak
- Okuma kopyaları: raporlamayı işlem yükünden ayırmak
- Büyük kiracıyı ayırmak: satır düzeyinde başlayıp büyüyen kiracıyı kendi veritabanına taşıyabilmek
İşletme
- Kiracı açma ve kapatma (veri silme dahil) otomatik olmalı
- İzleme metrikleri kiracı bazında ayrılabilmeli
- Bir kiracıdaki sorun ya da geri alma diğerlerini etkilememeli
Doğru kalıbı seçmek
Kiracı başına veritabanı:
- Sıkı düzenlenen sektörler, müşterinin verisinin fiziksel olarak ayrı durmasını sözleşmeyle istediği durumlar
- Kiracılar arasında veri hacmi çok farklıysa
Kiracı başına şema:
- Onlarca ya da birkaç yüz kiracı
- İzolasyon ile işletme kolaylığı arasında denge arıyorsanız
Satır düzeyinde izolasyon:
- Binlerce kiracı
- Maliyet öncelikliyse ve kiracılar birbirine benziyorsa
- RLS ve izolasyon testleriyle desteklenmek şartıyla
Pek çok ürün satır düzeyinde başlar ve büyük ya da düzenlenmiş kiracıları sonradan kendi veritabanına taşır. Bunu mümkün kılan, kiracı kimliğinin baştan her tabloda ve her olayda bulunmasıdır.
Çok kiracılı mimari tek kalıplık bir karar değil; iş modelinize, müşterilerinize, mevzuata ve ekibinize göre şekillenir. Bu kararı birlikte değerlendirmek isterseniz bize yazın.
ByteGurus
Yazar