Connection Pooling ve Performans İyileştirmeleri
Veritabanı bağlantısı açmak, TCP el sıkışması, kimlik doğrulama ve bellek tahsisi gibi maliyetli işlemleri beraberinde getirir. Her istekte bu maliyeti ödemek, yüksek trafikli uygulamalarda performansı ciddi şekilde düşürür. Connection pooling (bağlantı havuzu), açılan bağlantıları kapatmak yerine havuza geri göndererek yeniden kullanılmalarını sağlar; böylece bağlantı kurulum maliyeti yalnızca ilk seferde ödenir.
1. Connection Pooling Nasıl Çalışır?
Bir uygulama Open() çağrısı yaptığında, .NET önce ilgili connection string için bir havuz olup olmadığını kontrol eder. Havuzda boş bir bağlantı varsa, doğrudan o verilir; yoksa yeni bir fiziksel bağlantı açılır. Uygulama Close() veya Dispose() çağırdığında ise bağlantı gerçekten kapatılmaz, havuza geri döner .
Temel Kavramlar:
| Kavram | Açıklama |
|---|---|
| Min Pool Size | Havuzda her zaman tutulan minimum bağlantı sayısı |
| Max Pool Size | Havuzun büyüyebileceği maksimum bağlantı sayısı (varsayılan: 100) |
| Connection Timeout | Havuzdan bağlantı beklerken geçecek maksimum süre (varsayılan: 15 saniye) |
| Connection Lifetime | Bir bağlantının havuzda kalabileceği maksimum süre |
Kritik Kural: Havuzlar connection string bazında oluşturulur. Aynı veritabanına farklı connection string'lerle bağlanmak, ayrı havuzlar oluşturur ve kaynak israfına yol açar.
2. Havuz Tükenmesi (Pool Starvation): En Sık Karşılaşılan Sorun
Havuzdaki tüm bağlantılar kullanımdayken yeni bir talep geldiğinde, uygulama bağlantı boşalana kadar bekler. Bekleme süresi Connection Timeout değerini aşarsa şu hata alınır:
"Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool."
Nedenleri:
-
Bağlantı sızıntısı (leak):
Dispose()veyaClose()çağrılmamış bağlantılar havuza dönmez. -
Uzun süren sorgular: Bir bağlantı saatlerce meşgul kalıyorsa, diğer istekler bekler.
-
Yetersiz havuz boyutu: Eşzamanlı istek sayısı
Max Pool Size'ı aşıyorsa. -
Aşırı büyük
Max Pool Size: Görünüşte çözüm gibi görünse de, veritabanı sunucusunun bağlantı limitini aşarak yeni sorunlara yol açar.
Çözüm Öncelikleri:
-
Bağlantıların her zaman
usingbloğu içinde açılması (en kritik adım). -
Uzun süren sorguların optimize edilmesi.
-
Havuz boyutunun, eşzamanlı istek sayısına ve veritabanı kapasitesine göre ayarlanması.
3. .NET'te Doğru Kullanım
Doğru Kullanım (using ile):
csharp
public async Task<Order> GetOrderAsync(int id)
{
await using var connection = new SqlConnection(_connectionString);
await connection.OpenAsync();
// Sorgu çalıştır...
return order;
} // Bağlantı otomatik olarak havuza döner
Yanlış Kullanım (sızıntı):
csharp
var connection = new SqlConnection(_connectionString); await connection.OpenAsync(); // Dispose çağrılmazsa bağlantı havuza dönmez!
EF Core'da Pooling:
EF Core, DbContext yaşam döngüsünü DI ile yönetir; Scoped olarak kaydedilen context, her istek sonunda otomatik dispose edilir ve bağlantı havuza döner. Ancak DbContextPool kullanmak, context nesnesinin kendisini de yeniden kullanarak ek kazanç sağlar :
csharp
builder.Services.AddDbContextPool<AppDbContext>(options =>
options.UseSqlServer(connectionString));
4. Performans İyileştirme İpuçları
| İpucu | Açıklama |
|---|---|
Min Pool Size ayarlayın |
Trafik ani artışlarında havuzun hazır bağlantıya sahip olmasını sağlar; ilk isteklerdeki gecikmeyi azaltır. |
Max Pool Size'ı abartmayın |
Veritabanı sunucusunun kapasitesini aşan havuz, bağlantı hatalarına ve kaynak tükenmesine yol açar. |
| Bağlantıları kısa tutun | Bağlantıyı aç, sorguyu çalıştır, kapat. Uzun süre açık tutmak, diğer isteklerin erişimini engeller. |
Connection Lifetime kullanın |
Yük dengeleme (load balancing) ortamlarında, bağlantıların belirli süre sonra yenilenmesini sağlar. |
| Async API'leri kullanın | OpenAsync, ExecuteReaderAsync gibi asenkron metotlar, thread'i bloke etmeden bağlantı yönetimini sağlar. |
5. İzleme ve Tanılama
Havuzun sağlığını izlemek için .NET'te çeşitli sayaçlar mevcuttur:
| Sayaç | Anlamı |
|---|---|
| Pooled Connections | Havuzdaki toplam bağlantı sayısı |
| Active Connections | Şu anda kullanımda olan bağlantı sayısı |
| Free Connections | Havuzda boş bekleyen bağlantı sayısı |
| Connection Timeouts | Zaman aşımına uğrayan bağlantı talepleri |
Bu metrikler, dotnet-counters veya Application Insights gibi araçlarla izlenebilir. Aktif bağlantı sayısı sürekli Max Pool Size'a yakınsa, havuz tükenmesi riski vardır.
6. En İyi Pratikler
| Pratik | Açıklama |
|---|---|
Her zaman using kullanın |
Bağlantıların istisna durumunda bile havuza dönmesini garanti eder. |
| Connection string'i standartlaştırın | Aynı veritabanına farklı string'lerle bağlanmak, ayrı havuzlar oluşturur. |
| Bağlantı süresini kısa tutun | "Aç, kullan, kapat" prensibi; uzun süreli bağlantılar havuzu tüketir. |
| Timeout değerlerini gözden geçirin | Varsayılan 15 saniye çoğu senaryo için uygundur; ancak yavaş veritabanlarında artırılabilir. |
| Havuz metriklerini izleyin | Aktif/boş bağlantı oranı, tükenme riskinin erken göstergesidir. |
Sonuç
Connection pooling, veritabanı performansının görünmez kahramanıdır: doğru yapılandırıldığında, bağlantı maliyetini ortadan kaldırır ve uygulamanın ölçeklenebilirliğini artırır. Ancak yanlış kullanıldığında, havuz tükenmesi ve timeout hatalarıyla uygulamayı çökertir. En kritik kural, bağlantıların her zaman using bloğu içinde açılması ve kısa süreli tutulmasıdır. Havuz boyutunu veritabanı kapasitesine göre ayarlamak ve metrikleri izlemek, sorunları kullanıcılar fark etmeden önce tespit etmenizi sağlar. Unutmayın: En iyi bağlantı, ihtiyaç duyulduğunda hazır olan; en kötü bağlantı ise asla havuza dönmeyendir.