---
title: 'Bağlantı Havuzlayıcıları'
source: 'https://academia.sh/tr/kurslar/veritabani-yonetimi/baglanti-havuzlayicilari'
course: 'İlişkisel Veritabanı Yönetimi'
language: tr
updated: '2026-08-17T18:08:56+00:00'
license: 'CC BY-SA 4.0'
---

# Bağlantı Havuzlayıcıları

Bağlantı sayısının yönetilmesi: bağlantı açmanın ölçülen maliyeti, oturum ve işlem düzeyinde havuzlamanın farkı, havuzdan devralınan oturum durumunun satır düzeyi güvenliği nasıl delebildiği ve havuz boyutunun kuyruk süresiyle ilişkisi.

Devralmanın son adımı uygulamaların yeni birincile yönlendirilmesiydi; bu, uygulamanın
veritabanına doğrudan değil, araya giren bir katman üzerinden bağlandığı anlamına gelir.
Aynı katman, arıza olmadan da her gün karşılaşılan ikinci bir sorunu çözer.

Sorun şudur: uygulama sunucularının sayısı arttıkça bağlantı sayısı da artar. Her uygulama
örneği kendi bağlantılarını açtığında, on örnek ve örnek başına yirmi bağlantı iki yüz
bağlantı eder. Veritabanı motoru için bağlantı ucuz bir nesne değildir — her biri bellek
tüketir, çoğu motorda ayrı bir süreç ya da iş parçacığı gerektirir ve boşta beklerken bile
maliyeti sürer. Bağlantı sayısı belirli bir eşiği aştığında motor yavaşlar, sonra yeni
bağlantı kabul etmeyi bırakır.

## Bir Bağlantının Maliyeti

Bir bağlantının açılması, uzaktan bakıldığında tek bir işlem gibi görünür. İçeride birkaç
adım vardır: taşıma katmanı bağlantısının kurulması, şifreli oturumun anlaşması, kimlik
doğrulama, sunucu tarafında bir süreç ya da iş parçacığının başlatılması, oturum
durumunun hazırlanması. Bunların hiçbiri veri okumaz.

Aşağıdaki blok bu maliyetin **en düşük** biçimini ölçüyor: dosya tabanlı bir motorda ağ
yoktur, şifreleme yoktur, kimlik doğrulama yoktur, sunucu tarafında süreç başlatılmaz.
Ölçülen şey yalnız dosyanın açılması ve şema bilgisinin okunmasıdır. Sunucu tabanlı bir
motorda bu maliyetin üzerine sayılan adımlar eklenir; ölçüm bir taban değer verir, üst
değer değil. Süreler makineye bağlıdır.

```bash
rm -f havuz.db

node - <<'EOF'
const { DatabaseSync } = require("node:sqlite");
const SORGU = "SELECT COUNT(*) c FROM odunc WHERE uye_id=?";
const N = 3000;

const kur = new DatabaseSync("havuz.db");
kur.exec("CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, alis TEXT)");
kur.exec("CREATE INDEX odunc_uye ON odunc(uye_id)");
const ekle = kur.prepare("INSERT INTO odunc(kitap_id,uye_id,alis) VALUES(?,?,?)");
kur.exec("BEGIN");
for (let i = 1; i <= 20000; i++) ekle.run(i % 400, i % 250, "2025-06-01");
kur.exec("COMMIT");
kur.close();

const olc = (ad, is) => {
  const t = process.hrtime.bigint();
  is();
  const ms = Number(process.hrtime.bigint() - t) / 1e6;
  console.log(ad.padEnd(38) + " | " + ms.toFixed(1).padStart(9) + " | " +
              (ms * 1000 / N).toFixed(1).padStart(11));
};

console.log("yol                                    | toplam ms | istek basi us");
console.log("---------------------------------------|-----------|--------------");
olc("her istekte yeni baglanti + deyim", () => {
  for (let i = 0; i < N; i++) {
    const db = new DatabaseSync("havuz.db");
    db.prepare(SORGU).get(i % 250);
    db.close();
  }
});
const paylasilan = new DatabaseSync("havuz.db");
olc("paylasilan baglanti, her istekte deyim", () => {
  for (let i = 0; i < N; i++) paylasilan.prepare(SORGU).get(i % 250);
});
const hazir = paylasilan.prepare(SORGU);
olc("paylasilan baglanti, hazir deyim", () => {
  for (let i = 0; i < N; i++) hazir.get(i % 250);
});
EOF
```

```text
yol                                    | toplam ms | istek basi us
---------------------------------------|-----------|--------------
her istekte yeni baglanti + deyim      |     161.7 |        53.9
paylasilan baglanti, her istekte deyim |      18.2 |         6.1
paylasilan baglanti, hazir deyim       |      11.7 |         3.9
```

Aynı sorgu üç yolla çalıştırıldı ve istek başına süre on dört kata yakın değişti.
Sorgunun kendisi her üç satırda da aynı işi yapıyor; fark, sorgunun etrafındaki
hazırlıktır.

İkinci ile üçüncü satır arasındaki fark ayrı bir konuyu gösterir: **hazır deyim (prepared
statement)** yeniden kullanıldığında ayrıştırma ve planlama işi tekrarlanmaz. Bu, İleri SQL
kursunda plan önbelleği bağlamında ele alınan kazancın işletim tarafındaki karşılığıdır.

**Bağlantı havuzu (connection pool)** bu tabloyu birinci satırdan üçüncüye taşır. Havuz,
önceden açılmış bir bağlantı kümesi tutar; uygulama bağlantı istediğinde havuzdan ödünç
alır, işi bitince geri verir. Bağlantı kapanmaz, bir sonraki istek onu hazır bulur.

## Havuzlama Düzeyleri

Havuzun bağlantıyı ne zaman geri aldığı, uygulamanın nelerden vazgeçmesi gerektiğini
belirler. Üç düzey vardır.

**Oturum düzeyinde havuzlama**, bağlantıyı istemci bağlantısı kapanana kadar aynı istemciye
verir. Uygulama açısından hiçbir şey değişmez; oturuma bağlı her özellik çalışır. Karşılığı,
eşzamanlı istemci sayısı kadar veritabanı bağlantısı gerekmesidir — bağlantı sayısı sorunu
tam olarak çözülmez.

**İşlem düzeyinde havuzlama**, bağlantıyı yalnız bir işlem süresince verir; işlem bitince
bağlantı havuza döner ve başka bir istemciye gidebilir. Az sayıda bağlantıyla çok sayıda
istemciye hizmet edilmesini bu düzey sağlar. Karşılığı, oturuma ait her şeyin
güvenilmez hâle gelmesidir: geçici tablolar, oturum değişkenleri, oturum düzeyindeki
kilitler ve bağlantıya iliştirilmiş hazır deyimler.

**Deyim düzeyinde havuzlama** bağlantıyı her deyimden sonra geri alır. Çok istemcili
kurulumlarda en yüksek paylaşımı verir; çok deyimli işlem yazılamadığı için uygulamanın
işlem kullanamamasına yol açar.

İşlem düzeyindeki havuzlamanın kısıtı bir başarım ayrıntısı değildir; güvenlik sonucu
doğurabilir. Aşağıdaki blok bunu Satır Düzeyi Güvenlik dersindeki düzenek üzerinde
gösteriyor: politika oturum değişkenine bakar, bağlantı ise iki istek arasında el
değiştirir.

```bash
rm -f masa.db

node - <<'EOF'
const { DatabaseSync } = require("node:sqlite");

const db = new DatabaseSync("masa.db");   // havuzdaki tek baglanti
db.exec(`
CREATE TABLE odunc(id INTEGER PRIMARY KEY, kitap_id INT, uye_id INT, sube_id INT, alis TEXT);
CREATE TABLE oturum(anahtar TEXT PRIMARY KEY, deger TEXT NOT NULL);
`);
const ekle = db.prepare("INSERT INTO odunc(kitap_id,uye_id,sube_id,alis) VALUES(?,?,?,?)");
db.exec("BEGIN");
for (let i = 1; i <= 900; i++)
  ekle.run(i % 400, i % 250, i <= 500 ? 1 : (i <= 800 ? 2 : 3), "2025-06-01");
db.exec("COMMIT");

// Politika oturum degiskenine bakar (Satir Duzeyi Guvenlik dersindeki dizenek).
const gorunen = () => db.prepare(
  "SELECT COUNT(*) c FROM odunc WHERE sube_id=" +
  "(SELECT CAST(deger AS INTEGER) FROM oturum WHERE anahtar='sube_id')").get().c;
const ayarla = (sube) => db.prepare(
  "INSERT INTO oturum VALUES('sube_id',?)" +
  " ON CONFLICT(anahtar) DO UPDATE SET deger=excluded.deger").run(String(sube));
const gercek = (s) => db.prepare("SELECT COUNT(*) c FROM odunc WHERE sube_id=?").get(s).c;

console.log("subelerin gercek kayit sayisi: 1 -> " + gercek(1) + ", 2 -> " + gercek(2));
console.log();

// 1. istek: 1 numarali subenin masasi baglantiyi alir.
ayarla(1);
console.log("1. istek (sube 1) gorunen satir      : " + gorunen());

// Baglanti havuza geri verilir. Havuz oturum durumunu temizlemiyor.
console.log("2. istek (sube 2), oturum ayarlanmadi: " + gorunen() +
            "  <- sube 1'in satirlari");

// Ayni sira, havuz devirde oturum durumunu temizliyor.
db.exec("DELETE FROM oturum");
console.log("2. istek (sube 2), havuz temizledi   : " + gorunen() +
            "  <- hicbir satir gorunmuyor");
ayarla(2);
console.log("2. istek (sube 2), oturum ayarlandi  : " + gorunen());
EOF
```

```text
subelerin gercek kayit sayisi: 1 -> 500, 2 -> 300

1. istek (sube 1) gorunen satir      : 500
2. istek (sube 2), oturum ayarlanmadi: 500  <- sube 1'in satirlari
2. istek (sube 2), havuz temizledi   : 0  <- hicbir satir gorunmuyor
2. istek (sube 2), oturum ayarlandi  : 300
```

İkinci satır sorunun kendisidir. İkinci şubenin masası üç yüz kayıt görmesi gerekirken beş
yüz kayıt görüyor; gördükleri başka bir şubenin ödünç kayıtları. Politika doğru yazılmış,
sorgu doğru, veri doğru — yanlış olan tek şey, bağlantının önceki kullanıcının oturum
değerini taşımasıdır.

Üçüncü satır çözümü gösterir: havuz, bağlantıyı devrederken oturum durumunu temizler.
Temizlenmiş durumda politika hiçbir satır döndürmez. Bu davranış istenen davranıştır —
**kapalıya düşmek**: bilgi eksikse veri gösterilmez. Uygulamanın her isteğin başında oturum
değerini yeniden ayarlaması, dördüncü satırdaki doğru sonucu verir.

Buradan çıkan işletim kuralı, işlem düzeyinde havuzlama kullanan her kurulum için geçerlidir:
oturuma bağlı hiçbir bilgi istekler arasında yaşadığı varsayılmadan yazılır ve havuzun
devir temizliği yapılandırmadan doğrulanır.

## Havuz Boyutu ve Kuyruk

Havuz boyutu, veritabanına aynı anda kaç isteğin girebileceğini belirler. Boyut küçükse
istekler kuyrukta bekler; büyükse motorda eşzamanlı çalışan işlem sayısı artar ve işlemler
birbirini yavaşlatır — kilit çekişmesi, önbellek paylaşımı ve zamanlayıcı yükü nedeniyle.

Aşağıdaki blok bunu benzetimle inceliyor. Gelişler, taban işlem süresi ve çekişme katsayısı
**model değerleridir**; gerçek bir veritabanı çalıştırılmıyor. Çekişmenin biçimi de bir
varsayımdır: eşzamanlı işlem sayısı çekirdek sayısını aştıkça her işlemin süresi doğrusal
büyüyor. Gerçek bir sistemde bu eğri ölçülerek bulunur. Modelin gösterdiği şey eğrinin
**bir en iyisi olduğudur**.

```bash
node - <<'EOF'
// Model: istekler havuzdan bir baglanti bekler, sonra veritabaninda calisir.
// Es zamanli islem sayisi cekirdek sayisini astikca her islem yavaslar (cekisme).
const TABAN = 10, CEKIRDEK = 8, CEKISME = 0.25;   // ms, adet, katsayi
const HIZ = 700, SURE = 30000, ADIM = 0.25;       // istek/sn, ms, ms
const ZAMAN_ASIMI = 200;                          // ms

let tohum = 20250616;
const rastgele = () => (tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648;
const gelis = [];
for (let t = 0; t < SURE; ) {
  t += -Math.log(1 - rastgele()) * 1000 / HIZ;
  if (t < SURE) gelis.push(t);
}

console.log("yuk: " + HIZ + " istek/sn, taban islem suresi " + TABAN + " ms, " +
            CEKIRDEK + " cekirdek, " + gelis.length + " istek");
console.log();
console.log("havuz | tamamlanan | kuyrukta | islemde | toplam  | zaman");
console.log("boyu  | istek      | (ort ms) | (ort ms)| (ort ms)| asimi");
console.log("------|------------|----------|---------|---------|-------");
for (const havuz of [2, 4, 8, 16, 32, 64]) {
  const kuyruk = [], aktif = [];
  let sonraki = 0, biten = 0, kuyrukToplam = 0, islemToplam = 0, asim = 0;
  for (let t = 0; t < SURE * 1.5; t += ADIM) {
    while (sonraki < gelis.length && gelis[sonraki] <= t) kuyruk.push(gelis[sonraki++]);
    while (kuyruk.length && aktif.length < havuz) {
      const g = kuyruk.shift();
      if (t - g > ZAMAN_ASIMI) { asim++; continue; }
      aktif.push({ bekleme: t - g, kalan: TABAN, basladi: t });
    }
    const yavaslik = 1 + CEKISME * Math.max(0, aktif.length - CEKIRDEK);
    for (const i of aktif) i.kalan -= ADIM / yavaslik;
    for (let i = aktif.length - 1; i >= 0; i--) {
      if (aktif[i].kalan > 0) continue;
      kuyrukToplam += aktif[i].bekleme;
      islemToplam += t - aktif[i].basladi;
      biten++; aktif.splice(i, 1);
    }
    if (sonraki >= gelis.length && !kuyruk.length && !aktif.length) break;
  }
  console.log(String(havuz).padStart(5) + " | " + String(biten).padStart(10) + " | " +
    (kuyrukToplam / biten).toFixed(1).padStart(8) + " | " +
    (islemToplam / biten).toFixed(1).padStart(7) + " | " +
    ((kuyrukToplam + islemToplam) / biten).toFixed(1).padStart(7) + " | " +
    String(asim).padStart(6));
}
EOF
```

```text
yuk: 700 istek/sn, taban islem suresi 10 ms, 8 cekirdek, 21200 istek

havuz | tamamlanan | kuyrukta | islemde | toplam  | zaman
boyu  | istek      | (ort ms) | (ort ms)| (ort ms)| asimi
------|------------|----------|---------|---------|-------
    2 |       6040 |    197.4 |     9.8 |   207.2 |  15160
    4 |      12080 |    196.4 |     9.8 |   206.1 |   9120
    8 |      21200 |      3.9 |     9.8 |    13.6 |      0
   16 |      16116 |    192.9 |    29.7 |   222.6 |   5084
   32 |      13823 |    193.5 |    69.6 |   263.1 |   7377
   64 |      12919 |    190.9 |   149.2 |   340.1 |   8281
```

Tablo iki ayrı arıza biçimini aynı sütunda gösteriyor. İki ve dört boyutlu havuzlarda
işlemde geçen süre en düşük değerdedir; sorun kuyruktadır. Havuzun geçirdiği iş, gelen
işten azdır ve isteklerin çoğu zaman aşımına uğrar. Bu **havuz doygunluğudur**.

On altı ve üzeri boyutlarda kuyruk aynı derecede kötüdür, ama nedeni başkadır: işlemde
geçen süre üç kattan on beş kata kadar büyümüştür. Havuz daha çok isteği içeri aldı,
içeridekiler birbirini yavaşlattı ve geçen toplam iş azaldı. Havuzu büyütmek burada
durumu kötüleştirdi.

Sekiz boyutlu havuz iki uçtan da uzaktır: bekleme dört milisaniye, toplam yanıt on üç
milisaniye, zaman aşımı yok. Bu sayı çekirdek sayısıyla aynıdır ve rastlantı değildir —
en iyi havuz boyutu, motorun gerçekten paralel yürütebildiği iş miktarına yakındır. Disk
beklemesi baskın olan iş yüklerinde bu sayı çekirdek sayısının üzerine çıkar; işlemci
baskın iş yüklerinde çekirdek sayısında kalır. Doğru değeri veren tek yol ölçmektir.

Havuz boyutu tek bir uygulama örneği için değil, sistem bütünü için düşünülür. Toplam
bağlantı sayısı, havuz boyutu ile uygulama örneği sayısının çarpımıdır; on örnekli bir
kurulumda örnek başına yirmi bağlantı, motora iki yüz bağlantı olarak gelir. Uygulama
içindeki havuzların bu çarpımı görmemesi, ortada tek bir havuzlayıcı bulundurmanın başlıca
gerekçesidir.

Son bir ayar, boşta bekleyen bağlantıların ömrüdür. Yük düştüğünde havuzun küçülmesi
kaynak boşaltır; her seferinde yeniden açmak ise birinci tablodaki maliyeti geri getirir.
İki uç arasındaki denge, yükün gün içindeki değişimine göre kurulur.

## Özet

- Bağlantı açmak, sorgunun kendisinden bağımsız bir hazırlık maliyeti taşır; ölçümde aynı
  sorgu, yeni bağlantı ve hazır deyim yolları arasında on dört kata yakın fark verdi.
- Havuz, önceden açılmış bağlantıları ödünç vererek bu maliyeti ortadan kaldırır ve motora
  gelen bağlantı sayısını sınırlar.
- Oturum düzeyinde havuzlama uygulamayı kısıtlamaz ama bağlantı sayısını azaltmaz; işlem
  düzeyinde havuzlama azaltır ve oturuma bağlı her şeyi güvenilmez kılar.
- Havuzdan devralınan oturum değişkeni, satır düzeyi güvenlik politikasının başka bir
  hesabın satırlarını göstermesine yol açabilir; havuzun devir temizliği doğrulanmalıdır.
- Havuz boyutu küçükse kuyruk büyür, büyükse eşzamanlı işlemler birbirini yavaşlatır; en
  iyi değer motorun paralel yürütebildiği iş miktarına yakındır ve ölçülerek bulunur.
- Toplam bağlantı sayısı havuz boyutu ile uygulama örneği sayısının çarpımıdır.

## Sonraki Adım

Bu dersteki her karar bir ölçüme dayanıyordu: bağlantı maliyeti, kuyruk süresi, eşzamanlı
işlem sayısı, zaman aşımına uğrayan istek oranı. Bunların hiçbiri kendiliğinden görünmez;
havuz doygunluğu, uygulama tarafında yalnız "veritabanı yavaş" olarak fark edilir ve
yanlış yerde aranır. Sonraki ders izlemeyi ele alıyor: hangi büyüklüklerin toplanacağı,
ortalamanın neden yanıltıcı olduğu ve yüzdelik dilimlerin ne gösterdiği, yavaş sorguların
eşikle yakalanması, bekleme olaylarının darboğazı nasıl adlandırdığı ve bir uyarının ne
zaman anlamlı olduğu.
