Database Sharding ve Partitioning: Devasa Veri Kümelerini Bölme Yöntemleri
Uygulamanız büyüdükçe, veritabanınız da büyür. Bir noktada, tek bir veritabanı sunucusunun depolama kapasitesi, işlem gücü (CPU) veya g/ç (I/O) sınırlarına ulaşırsınız. Bu aşamada, verileri birden fazla sunucuya veya tabloya yaymak (dağıtmak) için Sharding ve Partitioning devreye girer. Bu yazıda, bu iki kavram arasındaki farkı, sharding stratejilerini, partition key seçimini ve dağıtık ortamda karşılaşacağınız zorlukları ve çözümlerini ele alıyoruz.
1. Sharding ve Partitioning: Temel Fark
Her iki kavram da veriyi "bölme" işlemidir, ancak kapsam ve amaç farklıdır.
-
Partitioning (Bölümlendirme): Veritabanınızın aynı fiziksel sunucu (veya aynı veritabanı instance'ı) içinde, tabloları mantıksal olarak daha küçük parçalara (partitions) ayırmaktır. Örneğin, bir
Orderstablosunu yıl bazında (2023, 2024, 2025) ayırmak. Partitioning, genellikle veritabanı yönetim sistemi (DBMS) tarafından yerel olarak desteklenir (SQL Server, PostgreSQL, MySQL). -
Sharding (Yatay Parçalama): Veritabanını birden fazla bağımsız sunucuya (fiziksel veya sanal) dağıtmaktır. Her sunucu, verinin bir kısmından (shard) sorumludur. Sharding, uygulama katmanı veya ara katman (middleware) tarafından yönetilir. "Sharding = Horizontal Partitioning Across Servers" olarak düşünülebilir.
Bu yazıda ağırlıklı olarak Sharding'e odaklanacağız, ancak partition key (shard key) seçimi her iki yaklaşım için de kritiktir.
2. Sharding Stratejileri (Veri Dağıtım Yöntemleri)
Veriyi sunuculara dağıtmanın birkaç temel yolu vardır. Her birinin avantajları ve dezavantajları farklıdır.
A. Range-Based Sharding (Aralık Tabanlı)
Veri, belirli bir aralığa (range) göre dağıtılır. Örneğin, kullanıcı ID'sine göre: 1-1000 arası Shard A, 1001-2000 arası Shard B.
text
Shard A: UserId 1-1000 Shard B: UserId 1001-2000 Shard C: UserId 2001-3000
-
Artıları: Sorgulaması kolaydır (
WHERE UserId BETWEEN 500 AND 600tek bir shard'a gider). Ekleme (insert) işlemleri hızlıdır (son aralığa ekler). -
Eksileri: Hotspot (Sıcak Nokta) sorunu yaratır: En yeni kayıtlar (ör. yüksek ID'li) sürekli aynı shard'a yazılır. Shard'lar arası dengesizlik (ör. Shard A çok dolu, Shard C boş) olabilir.
-
Kullanım Alanı: Zaman serisi (tarih aralığı) verileri, log verileri.
B. Hash-Based Sharding (Karma Tabanlı)
Bir shard key (ör. UserId) hash fonksiyonundan geçirilir ve sonucun mod (modulo) işlemi ile hangi shard'a gideceği belirlenir.
csharp
int shardIndex = Math.Abs(UserId.GetHashCode()) % numberOfShards;
-
Artıları: Veriyi oldukça dengeli (uniform) dağıtır. Hotspot sorununu büyük ölçüde çözer.
-
Eksileri: Range sorguları (
BETWEEN) zorlaşır. Tüm shard'lara sorgu göndermek gerekebilir (scatter-gather). Shard sayısı arttığında yeniden dengeleme (rebalancing) çok zordur (tüm veriler yeniden hash'lenir). -
Kullanım Alanı: Kullanıcı profilleri, e-ticaret ürünleri, sosyal medya verileri.
C. List-Based Sharding (Liste Tabanlı)
Belirli bir anahtar değerine göre shard atanır. Örneğin, ülke koduna göre.
text
Shard A: TR, AZ, GE Shard B: DE, FR, IT Shard C: US, CA, MX
-
Artıları: Coğrafi dağılım (geo-distribution) için idealdir. Veri yerelliği (data locality) sağlar.
-
Eksileri: Dengesiz dağılım olabilir (büyük ülkeler daha fazla veri üretir). Yeni ülke eklendiğinde shard haritası güncellenmelidir.
D. Directory-Based Sharding (Dizin Tabanlı)
Bir dizin (lookup) tablosu veya hizmeti (örn. Redis, Consul) hangi shard key'in hangi shard'a ait olduğunu saklar.
text
UserId: 12345 -> Shard B UserId: 67890 -> Shard A
-
Artıları: En esnek yöntemdir. Shard sayısını artırmak yeniden hash gerektirmez (sadece dizin güncellenir).
-
Eksileri: Dizin ek bir bağımlılık ve performans yükü getirir (her sorguda dizine bakılır). Dizin tek bir nokta olursa (single point of failure) tüm sistem çöker.
3. Partition Key (Shard Key) Seçimi: Hayati Bir Karar
Shard key seçimi, sisteminizin performansını ve ölçeklenebilirliğini belirleyen en kritik karardır. İyi bir shard key:
-
Yüksek Kardinaliteye (High Cardinality) Sahip Olmalı: Mümkün olduğunca benzersiz değerler üretmeli (örn.
UserId,CustomerId,OrderId). Çok az benzersiz değer (örn.Gender,Country) dengesiz dağılıma ve hotspot'a neden olur. -
Sorgu Desenine Uygun Olmalı: Sorguların büyük çoğunluğu shard key üzerinden filtrelenebilmelidir. Örneğin, tüm sorgular
UserIdiçeriyorsa,UserIdshard key olmalıdır. -
Sabit Kalmalı: Shard key, bir kaydın ömrü boyunca asla değişmemelidir. Shard key değişirse, tüm veriyi başka bir shard'a taşımak gerekir (çok maliyetli).
-
Yazma Dağılımı Sağlamalı: Yazmalar tüm shard'lara eşit dağılmalıdır (hash-based en iyisidir).
Kötü Shard Key Örnekleri:
-
Status(Aktif/Pasif) - Tüm aktif kullanıcılar aynı shard'ta, pasifler boş. -
CreatedDate(Gün) - Tüm bugünkü kayıtlar aynı shard'ta. -
Country- Amerika çok daha fazla veri üretir.
4. Sharding Yönetim Zorlukları ve Çözümleri
Sharding, uygulamaya ve operasyonlara önemli zorluklar ekler.
| Zorluk | Açıklama | Çözüm |
|---|---|---|
| Cross-Shard Sorgular (Join) | Farklı shard'lardaki verileri birleştirmek (JOIN) zordur ve çok yavaştır. | Veriyi shard key'e göre tasarlayarak JOIN ihtiyacını en aza indirin. Denormalizasyon (fazlalık veri) kabul edin. Uygulama katmanında JOIN yapın (scatter-gather). |
| Distributed Transaction | Farklı shard'lardaki verileri güncellemek ACID uyumlu değildir. | Eventual Consistency (Nihai Tutarlılık) kabul edin. Saga Pattern veya Outbox Pattern kullanın. İki fazlı commit (2PC) performansı öldürür, kaçının. |
| Yeniden Dengeleme (Rebalancing) | Shard sayısı arttığında, veriyi yeniden dağıtmak (rebalancing) çok maliyetlidir. | Consistent Hashing (Tutarlı Karma) kullanın. Hash ring ile sadece küçük bir kısım veri taşınır. Directory-based sharding ile yeniden hash'e gerek kalmaz. |
| Shard Key Güncelleme | Shard key değeri değişirse, verinin fiziksel olarak başka bir shard'a taşınması gerekir. | Shard key'i asla güncellenemez (immutable) yapın. Eğer zorunluysa, eski değeri koruyup yeni bir kayıt oluşturun (soft delete + insert). |
| Single Point of Failure | Shard'lardan biri çökerse, o shard'daki tüm verilere erişilemez. | Her shard'ı replica (ikincil) ile çoğaltın. Read replica'lar ile okuma yükünü dağıtın. MongoDB, Cassandra gibi native sharding/replication desteği olan veritabanlarını tercih edin. |
5. .NET ve EF Core ile Sharding Yaklaşımları
EF Core, native olarak sharding desteği sunmaz. Ancak çeşitli yaklaşımlar vardır:
-
Uygulama Katmanında (Application-Level) Yönlendirme:
En yaygın yöntem. Veritabanı bağlantısını (DbContext) shard key'e göre seçersiniz.
csharp
public class ShardDbContextFactory
{
private readonly Dictionary<int, string> _shardConnections = new()
{
{ 0, "Server=shard0.db;Database=MyDb;" },
{ 1, "Server=shard1.db;Database=MyDb;" },
{ 2, "Server=shard2.db;Database=MyDb;" }
};
public AppDbContext GetDbContext(int userId)
{
int shardIndex = Math.Abs(userId.GetHashCode()) % _shardConnections.Count;
var options = new DbContextOptionsBuilder<AppDbContext>()
.UseSqlServer(_shardConnections[shardIndex])
.Options;
return new AppDbContext(options);
}
}
// Repository/Service katmanında kullanım
public class OrderService
{
private readonly ShardDbContextFactory _factory;
public async Task<Order> GetOrderAsync(int orderId, int userId)
{
using var context = _factory.GetDbContext(userId);
return await context.Orders.FindAsync(orderId);
}
}
-
ShardingCore (Üçüncü Parti Kütüphane):
.NET için EF Core üzerinde sharding destekleyen bir kütüphanedir. Hash, range, mod gibi stratejiler sunar ve sorguları otomatik olarak ilgili shard'lara yönlendirir. -
Vitess (Proxy Yaklaşımı):
YouTube tarafından geliştirilen, MySQL için bir proxy (ara katman) çözümüdür. Uygulama Vitess'e sorgu gönderir; Vitess, sorguyu doğru shard'a yönlendirir. Cross-shard JOIN ve transaction'ları da yönetebilir. -
Distributed SQL (Yeni Nesil Veritabanları):
CockroachDB, YugabyteDB, Google Spanner gibi dağıtık SQL veritabanları, sharding'i otomatik olarak yönetir. Uygulama tek bir sunucuya bağlanıyormuş gibi davranır; veritabanı, veriyi arka planda otomatik olarak shard'lar. .NET'te bu veritabanları için sürücüler mevcuttur.
6. Sharding vs Partitioning: Ne Zaman Hangisi?
| Senaryo | Öneri |
|---|---|
| Veritabanı boyutu sunucu kapasitesini aşıyorsa | Sharding (sunucu sayısını artır) |
| Sorgu performansı düşükse (ör. yıllık veri arşivi) | Partitioning (aynı sunucuda böl) |
| Veri tutarlılığı (ACID) kritikse ve distributed transaction gerekiyorsa | Sharding'den kaçının, Partitioning veya daha güçlü bir sunucu (scale-up) düşünün. |
| Veri miktarı çok yüksek ve okuma/yazma dağılımı düzensizse | Hash-based Sharding ile dağılımı dengeleyin. |
| Coğrafi olarak dağınık kullanıcılar varsa | List-based Sharding (ülke/bölge) veya coğrafi olarak dağıtılmış Directory-based. |
Sonuç:
Database sharding, ölçeklenebilirlik için güçlü bir araçtır, ancak beraberinde ciddi karmaşıklık getirir. Shard key seçimi, hayatınızı kurtarabilir veya uygulamanızı mahvedebilir. Mümkünse, sharding'e başvurmadan önce diğer optimizasyonları (indexleme, sorgu optimizasyonu, önbellek, okuma replikaları) deneyin. Eğer sharding kaçınılmazsa, hash-based veya directory-based stratejileri tercih edin ve cross-shard sorgulardan kaçınmak için veri modelinizi buna göre tasarlayın. Sharding'i, ihtiyaç duyulmadan önce değil, ihtiyaç duyulduğunda uygulayın ve dağıtık sistemlerin getirdiği zorluklara (tutarlılık, yönetim, başarısızlık) hazırlıklı olun.