---
title: 'Yatay ve Dikey Ölçekleme'
source: 'https://academia.sh/tr/kurslar/backend-uretim/yatay-ve-dikey-olcekleme'
course: 'Sunucu Güvenliği ve Üretime Alma'
language: tr
updated: '2026-08-17T18:06:49+00:00'
license: 'CC BY-SA 4.0'
---

# Yatay ve Dikey Ölçekleme

İki ölçekleme ekseninin backend tarafındaki faturası: aynı yükün tek büyük süreçle ve dört küçük süreçle karşılanmasında süreç tabanının kaç kez ödendiği, hazır olma süresinin sıralı yayında nasıl çarpıldığı, her sürecin kendi bağlantı havuzunu açmasından doğan çarpan ve süreç sayısı artarken değişmeyen havuz ayarının depoya düşen toplam bağlantıyı sınırın üstüne taşıması.

Önceki iki ders tek bir süreçle çalıştı. Vekil tek bir hedefe iletti, süreç yöneticisi tek bir
işçiyi başlattı ve bellek sınırı o tek sürecin sınırıydı. Yük büyüdüğünde iki yol vardır: aynı
sürece daha çok kaynak vermek — **dikey ölçekleme** — ya da daha çok süreç açmak — **yatay
ölçekleme**.

İki eksenin hızlanma tarafı, seri payın koyduğu tavan ve kopya eklemenin azalan getirisi Sistem
Tasarımına Giriş kursunda kurulmuştu; burada girdi olarak alınır ve tekrarlanmaz. Bu dersin ölçtüğü
şey backend tarafındadır: iki yerleşim aynı yükü karşıladığında **arka planda ne kadar fazla kaynak
tutulduğu** ve süreç sayısı değiştiğinde hangi ayarın sessizce yanlışa döndüğü.

## Üç Yerleşim, Aynı Yük

Düzenek üç parçadan oluşuyor: bir kayıt deposu, ödünç uygulamasının bir ya da dört kopyası ve
kopyalar arasında sırayla dağıtan bir süreç.

- **DP1.** Depo eş zamanlı 16 bağlantı kabul eder; on yedincisi kurulur kurulmaz kapatılır. Bir
  sorgu 20 milisaniye sürer.
- **DP2.** Her uygulama kopyası başlarken havuzu kadar kalıcı bağlantı açar (ısınma). Hiçbiri
  kurulamazsa havuz boştur ve süreç isteği depoya hiç gitmeden reddeder.
- **DP3.** Kopyalar sırayla kaldırılır; havuzunu önce açan kopya bağlantıyı önce alır. Bellek süreç
  başına ortalanır, hazır olma süresi kopyaların en uzunudur; ikisi de 10'a yuvarlanır ve koşuma
  bağlıdır. Çarpanlar koşuma bağlı değildir.

```js
// topoloji/depo.mjs — kayit deposu. Es zamanli baglanti sayisinin bir ustu vardir; asan baglanti
// kurulur kurulmaz kapatilir. Sayaclar ayri bir noktadan okunur ki olcum baglantisi sayima girmesin.
import { createServer } from "node:http";

const [nokta, olcumNoktasi, sinir, sorguMs] = process.argv.slice(2, 6).map(Number);
let acik = 0, tepe = 0, reddedilen = 0, sorgu = 0;

if (Number.isInteger(nokta) === false) console.log("kullanim: node topoloji/depo.mjs <nokta> <olcum-noktasi> <baglanti-siniri> <sorgu-ms>");
else {
  const s = createServer((istek, cevap) => {
    sorgu += 1;
    cevap.sendDate = false;
    setTimeout(() => cevap.writeHead(200).end("kayit"), sorguMs);
  });
  s.on("connection", (soket) => {
    acik += 1;
    tepe = Math.max(tepe, acik);
    soket.on("close", () => { acik -= 1; });
    if (acik > sinir) { reddedilen += 1; soket.destroy(); }   // sinir asildi: baglanti kapatiliyor
  });
  s.listen(nokta, "127.0.0.1");
  createServer((istek, cevap) => {
    cevap.sendDate = false;
    cevap.writeHead(200).end(JSON.stringify({ tepe, reddedilen, sorgu }));
  }).listen(olcumNoktasi, "127.0.0.1");
}
```

Uygulama kopyası havuz boyunu bir argüman olarak alır. Bu sayı yerleşimin tek ayarıdır ve tek
süreçliyken doğru seçilmiştir.

```js
// topoloji/uygulama.mjs — odunc surecinin bir kopyasi. Baslarken depoya <havuz> kadar kalici
// baglanti acar (isinma). Hicbiri kurulamazsa havuz bostur ve surec istegi depoya hic gitmeden
// reddeder; kurulanlar varsa her istek havuzdaki bir baglantiyla karsilanir.
import { createServer, request, Agent } from "node:http";

const baslangic = Date.now();
const [nokta, depo, havuzBoyu] = process.argv.slice(2, 5).map(Number);
const havuz = new Agent({ keepAlive: true, maxSockets: havuzBoyu });
let hazirMs = -1, isinmaHatasi = 0, yanit = 0, hata = 0;

const depoya = (yol) => new Promise((coz) => {
  const r = request({ port: depo, path: yol, agent: havuz }, (y) => { y.resume(); y.on("end", () => coz(true)); });
  r.on("error", () => coz(false));
  r.end();
});

if (Number.isInteger(nokta) === false) console.log("kullanim: node topoloji/uygulama.mjs <nokta> <depo> <havuz>");
else {
  const sonuc = await Promise.all(Array.from({ length: havuzBoyu }, () => depoya("/isinma")));
  isinmaHatasi = sonuc.filter((b) => b === false).length;
  hazirMs = Date.now() - baslangic;
  createServer(async (istek, cevap) => {
    cevap.sendDate = false;
    if (istek.url === "/olcum") {
      const rss = Math.round(process.memoryUsage().rss / (1024 * 1024));
      cevap.writeHead(200).end(JSON.stringify({ rss, hazirMs, isinmaHatasi, yanit, hata }));
      return;
    }
    if (isinmaHatasi === havuzBoyu) { hata += 1; cevap.writeHead(503).end("havuz bos"); }
    else if (await depoya("/kayit")) { yanit += 1; cevap.writeHead(200).end("odunc"); }
    else { hata += 1; cevap.writeHead(503).end("depo yok"); }
  }).listen(nokta, "127.0.0.1");
}
```

```js
// topoloji/dagitici.mjs — istekleri kopyalar arasinda sirayla dagitir. Kopya listesi virgulle verilir.
import { createServer, request } from "node:http";

const [nokta, listeMetni] = [Number(process.argv[2]), process.argv[3] ?? ""];
const HEDEF = listeMetni.split(",").filter(Boolean).map(Number);
let sira = 0;

if (Number.isInteger(nokta) === false || HEDEF.length === 0) console.log("kullanim: node topoloji/dagitici.mjs <nokta> <hedef,hedef,...>");
else createServer((istekDisi, yanitDisi) => {
  yanitDisi.sendDate = false;
  const hedef = HEDEF[sira++ % HEDEF.length];
  const istekIc = request({ port: hedef, path: istekDisi.url, method: istekDisi.method }, (yanitIc) => {
    yanitDisi.writeHead(yanitIc.statusCode);
    yanitIc.pipe(yanitDisi);
  });
  istekIc.on("error", () => yanitDisi.writeHead(502).end("kopya yok"));
  istekDisi.pipe(istekIc);
}).listen(nokta, "127.0.0.1");
```

```js
// topoloji/yuk.mjs — <adet> istegi <esz> es zamanlilikla gonderir, sonra kopyalarin ve deponun
// sayaclarini toplar. Bellek ve hazir olma sureleri kopya basina okunup toplanir.
import { request } from "node:http";

const [nokta, adet, esz, depoOlcum, kopyaMetni, etiket] = [Number(process.argv[2]), Number(process.argv[3]), Number(process.argv[4]), Number(process.argv[5]), process.argv[6] ?? "", process.argv[7] ?? "-"];
const KOPYA = kopyaMetni.split(",").filter(Boolean).map(Number);
let yanit = 0, reddedilen = 0;

const bir = () => new Promise((coz) => {
  const r = request({ port: nokta, path: "/odunc" }, (y) => {
    y.resume();
    y.on("end", () => { if (y.statusCode === 200) yanit += 1; else reddedilen += 1; coz(); });
  });
  r.on("error", () => { reddedilen += 1; coz(); });
  r.end();
});

const oku = (p, yol) => new Promise((coz) => {
  request({ port: p, path: yol, agent: false }, (y) => { let b = ""; y.on("data", (d) => { b += d; }); y.on("end", () => coz(JSON.parse(b))); }).end();
});

if (Number.isInteger(nokta) === false) console.log("kullanim: node topoloji/yuk.mjs <nokta> <adet> <esz> <depo-olcum> <kopyalar> <etiket>");
else {
  let gonderilen = 0;
  await Promise.all(Array.from({ length: esz }, async () => { while (gonderilen++ < adet) await bir(); }));
  const k = await Promise.all(KOPYA.map((p) => oku(p, "/olcum")));
  const d = await oku(depoOlcum, "/olcum");
  const toplam = (alan) => k.reduce((t, x) => t + x[alan], 0);
  const P = KOPYA.length;
  // Surec basina bellek ortalamasi ve en uzun hazir olma suresi 10'a yuvarlanir, carpimla yazilir.
  const bellek = Math.round(toplam("rss") / P / 10) * 10;
  const hazir = Math.round(Math.max(...k.map((x) => x.hazirMs)) / 10) * 10;
  console.log(`${etiket} yanit ${String(yanit).padStart(2)}/${adet}  ` +
    `depo tepe ${String(d.tepe).padStart(2)} baglanti, ${String(d.reddedilen).padStart(2)} reddedilen  ` +
    `isinma hatasi ${String(toplam("isinmaHatasi")).padStart(2)}  ` +
    `bellek ${P}x${bellek}=${String(P * bellek).padStart(3)} MB  hazir ${P}x${hazir}=${String(P * hazir).padStart(3)} ms`);
}
```

Üç yerleşim aynı altmış isteği aynı eş zamanlılıkla karşılıyor. Birincisi tek süreç, havuz sekiz.
İkincisi dört süreç ve **aynı** havuz ayarı — süreç sayısı büyütülürken havuza dokunulmamış.
Üçüncüsü dört süreç, havuz iki.

```bash
# olc.sh — ayni yuk uc yerlesimle. Depo es zamanli 16 baglantiyi kabul ediyor, sorgu 20 ms.
# Kopyalar sirayla kaldiriliyor: havuzunu once acan kopya baglantiyi once aliyor.
kur() {                                   # kur <havuz> <kopya-sayisi>
  node topoloji/depo.mjs 8950 8959 16 20 & D=$!
  P=""; N=""
  for i in $(seq 1 "$2"); do
    node topoloji/uygulama.mjs $((8950 + i)) 8950 "$1" & P="$P $!"
    N="$N,$((8950 + i))"
    sleep 0.25
  done
  node topoloji/dagitici.mjs 8960 "${N#,}" & G=$!
  sleep 1
}
kapat() { kill $D $G $P 2>/dev/null; wait $D $G $P 2>/dev/null; sleep 0.3; }

kur 8 1; node topoloji/yuk.mjs 8960 60 32 8959 "8951" "tek surec, havuz 8 "; kapat
kur 8 4; node topoloji/yuk.mjs 8960 60 32 8959 "8951,8952,8953,8954" "dort surec, havuz 8 "; kapat
kur 2 4; node topoloji/yuk.mjs 8960 60 32 8959 "8951,8952,8953,8954" "dort surec, havuz 2 "; kapat
```

```
tek surec, havuz 8  yanit 60/60  depo tepe  8 baglanti,  0 reddedilen  isinma hatasi  0  bellek 1x60= 60 MB  hazir 1x30= 30 ms
dort surec, havuz 8  yanit 30/60  depo tepe 17 baglanti, 16 reddedilen  isinma hatasi 16  bellek 4x60=240 MB  hazir 4x30=120 ms
dort surec, havuz 2  yanit 60/60  depo tepe  8 baglanti,  0 reddedilen  isinma hatasi  0  bellek 4x60=240 MB  hazir 4x30=120 ms
```

Bellek ve süre sütunları koşuma bağlıdır; sayılar bu koşumdandır. Kopya sayısı, havuz boyu, depo
sınırı, istek sayısı ve reddedilen bağlantı sayısı koşumdan bağımsızdır.

## Taban Kaç Kez Ödenir

Birinci ve üçüncü satır aynı işi yapıyor: altmış isteğin altmışı yanıtlandı. Bellek toplamı 60
MB'den 240 MB'ye çıktı.

Fark isteğin kendisinden gelmiyor. Aynı altmış istek, aynı yirmi milisaniyelik sorgu, aynı yanıt.
Değişen şey **tabanın kaç kez ödendiğidir**: yorumlayıcı, yüklenen kod, olay döngüsü ve süreç
başına ayrılan yığın, süreç başına 60 MB tutuyor ve dört süreç bunu dört kez ödüyor. Çıktı bu
yüzden toplamı değil çarpımı yazıyor — okunması gereken sayı çarpandır.

İki eksenin faturası burada ayrışır. **Dikey ölçekleme tabanı bir kez öder**: süreç büyür, taban
sabit kalır, artan bellek işe gider. **Yatay ölçekleme tabanı süreç sayısı kadar öder**: dört
kopyaya geçmek, hiç istek gelmese bile 180 MB'yi işten önce harcar. Kopya sayısı arttıkça bu kalem
doğrusal büyür ve iş yapmaz.

Bu, yatay ölçeklemenin yanlış olduğunu söylemez; K01'in kopya tablosu yatay eksenin niçin
seçildiğini zaten kurmuştu. Söylediği şey, **karşılaştırmanın kaynak toplamı üzerinden yapılması
gerektiğidir**: dört küçük süreç, aynı toplam belleği kullanan tek bir büyük süreçten daha az iş
belleğine sahiptir.

## Hazır Olma Süresi

Bir kopya, havuzunu açıp isteğe hazır hâle gelene kadar 30 milisaniye harcıyor. Bu sayı üç
yerleşimde de aynı; kopya sayısı onu değiştirmiyor.

Değişen, o sürenin **kaç kez ödendiğidir**. Dört kopya koşut başlatıldığında duvar saati tek
kopyanınkine yakın kalır. Kopyalar sırayla kaldırıldığında — bir yayında kopyaların teker teker
değiştirilmesi tam olarak budur — süreler toplanır ve duvar saatine 120 milisaniye yazılır. Aynı
30 milisaniye, dört kopyada dört kez ödenir ve kopya sayısıyla doğrusal büyür.

Sayının kendisi küçüktür, çarpanı değildir. Isınmanın bir depo bağlantısı yerine yüklenecek bir
dizin, kurulacak bir bağlantı kümesi ya da ısıtılacak bir önbellek olduğu bir uygulamada aynı
çarpan saniyelere uygulanır; sıralı bir yayının süresi kopya sayısına bağlanır.

## Havuz Çarpanı

İkinci satır bu dersin asıl ölçüsüdür. Havuz ayarı değişmedi — tek süreçte doğru olan sekiz, dört
süreçte de sekiz. Değişen tek şey süreç sayısı.

Her süreç **kendi havuzunu** açar. Depoya düşen bağlantı sayısı süreç başına havuz değil, süreç
sayısı çarpı havuzdur:

| Yerleşim | Süreç | Süreç başına havuz | Depoya düşen | Depo sınırı | Sonuç |
|---|---|---|---|---|---|
| tek süreç | 1 | 8 | 8 | 16 | 60/60 yanıt |
| dört süreç, havuz 8 | 4 | 8 | 32 | 16 | 30/60 yanıt, 16 reddedilen bağlantı |
| dört süreç, havuz 2 | 4 | 2 | 8 | 16 | 60/60 yanıt |

İkinci satırda depo tepe olarak 17 bağlantı gördü — sınırın bir üstü, çünkü sınırı aşan bağlantı
sayılıp sonra kapatılıyor. Otuz iki bağlantı isteğinin on altısı kapatıldı; kapatılanların hepsi
sonradan kalkan iki kopyanın ısınma bağlantılarıydı.

Sonuç bu iki kopyanın **boş havuzla ayakta kalmasıdır**. Süreçleri çalışıyor, bağlantı noktalarını
dinliyor, süreç yöneticisi dört canlı süreç sayıyor ve dağıtıcı dört hedef görüyor. Altmış isteğin
otuzu bu iki kopyaya gidiyor ve depoya hiç ulaşmadan 503 alıyor. Ayakta olmakla iş yapabilmek
arasındaki fark, yerleşim ölçeklendiği anda açılıyor.

Bu ölçünün sessiz tarafı yapılandırmanın **nerede olmadığındadır**. Havuz boyu uygulama kodunda tek
bir sayıdır ve doğrudur. Süreç sayısı süreç yöneticisinde başka bir sayıdır ve o da doğrudur.
Depoya düşen toplam bağlantı **hiçbir yerde yazmaz**; iki ayarın çarpımıdır ve o çarpımı tutan bir
dosya yoktur. Kopya sayısını ikiden dörde çıkaran değişiklik havuz satırına dokunmaz, gözden
geçirmede havuz satırı görünmez ve sonuç bir hata iletisi olarak değil, depo tarafında kapanan
bağlantılar olarak ortaya çıkar.

Üçüncü satır düzeltmeyi gösteriyor: çarpım korunuyor. Dört süreç, havuz iki, depoya düşen sekiz.
Aynı altmış istek yeniden yanıtlanıyor. Düzeltme havuzu küçültmektir ve bedeli vardır — süreç
başına eş zamanlılık dörtte bire iner. Yatay ölçeklemenin depo tarafındaki gerçek kısıtı budur:
**kopya sayısı bağlantı bütçesini böler.**

## Ayarın Yeri

| Ayar | Yeri | Kaç yerde | Yanlış değerin sessiz sonucu |
|---|---|---|---|
| Kopya sayısı | süreç yöneticisi | 1 | bellek tabanı kopya sayısı kadar ödenir |
| Süreç başına havuz | uygulama kodu | 1 (her kopyada aynı) | çarpım depo sınırını aşar, bağlantılar sessizce kapanır |
| Depo bağlantı sınırı | depo | 1 | düşük sınır iyi istekleri, yüksek sınır depoyu düşürür |
| Isınma bağlantısı | uygulama kodu | 1 | havuzu kurulamayan kopya ayakta kalır ve payına düşen isteği reddeder |

Dört satırın ikisi tek başına doğru, birlikte yanlış olabilir. Kopya sayısı ile havuz boyu ayrı
dosyalarda durur, ayrı zamanlarda değişir ve aralarındaki çarpımı denetleyen bir yer yoktur.

## Özet

- Aynı altmış istek tek süreçte 60 MB, dört süreçte 240 MB tuttu; artan şey iş belleği değil, süreç
  tabanının dört kez ödenmesidir.
- Kopya başına 30 milisaniyelik hazır olma süresi kopya sayısıyla çarpılır; dört kopya sırayla
  kaldırıldığında duvar saatine 120 milisaniye yazılır.
- Süreç sayısı birden dörde çıkarılıp havuz ayarına dokunulmadığında depoya düşen bağlantı 8'den
  32'ye çıktı, depo 16 bağlantıyı kapattı, 16 ısınma isteği başarısız oldu ve altmış isteğin yalnız
  otuzu yanıtlandı.
- Havuzu kurulamayan iki kopya ayakta kaldı: süreç yöneticisi dört canlı süreç, dağıtıcı dört hedef
  sayarken bu iki kopya payına düşen otuz isteği depoya hiç gitmeden reddetti.
- Havuzu ikiye indirmek çarpımı sekizde tuttu ve altmış isteğin altmışı yeniden yanıtlandı; bedeli
  süreç başına eş zamanlılığın dörtte bire inmesidir.
- Depoya düşen toplam bağlantı iki ayarın çarpımıdır ve hiçbir dosyada yazmaz; iki ayar ayrı yerlerde
  durduğu için değişiklik gözden geçirmede görünmez.

## Sonraki Adım

Bu ders kopya sayısını bir ayar gibi ele aldı: süreç yöneticisine bir sayı yazıldı, dört süreç
kalktı, dağıtıcı istekleri sırayla paylaştırdı. Üç yerleşimin hepsinde altmış isteğin her biri
herhangi bir kopyaya gidebildi ve sonuç değişmedi. Bu, ölçeklemenin görünmeyen önkoşuludur ve
kendiliğinden sağlanmaz: kopyalar ancak isteğin hangisine gittiği fark etmiyorsa çoğaltılabilir.
Uygulama kodunda bellekte bir oturum, yerel diskte bir dosya, süreç içinde bir önbellek ya da
kurulmuş bir zamanlayıcı varsa kopyalar birbirinin eşi olmaktan çıkar. Sonraki ders bu bağların
kodda nerede durduğunu sayar: kaç çağrı yeri düğüme bağlıdır ve ikinci süreç eklendiğinde kaç istek
yanlış sonuç alır.
