---
title: 'Önbellek Ölçütleri'
source: 'https://academia.sh/tr/kurslar/eszamansiz-isleme/onbellek-olcutleri'
course: 'Önbellekleme, Kuyruklar ve Eşzamansız İşleme'
language: tr
updated: '2026-08-17T18:06:47+00:00'
license: 'CC BY-SA 4.0'
---

# Önbellek Ölçütleri

İsabet oranının hesaplanması ve ortalama yanıt süresine etkisi: kapasite arttıkça isabet oranının çizdiği eğri, tahliye sayısının anlamı, tek bir toplam oranın gizlediği ad alanı ayrışımı, bayt isabet oranı ile istek isabet oranının farkı ve düşük tekrarlı verinin önbellekten çıkarılmasının ölçülen kazancı.

Konu boyunca her ders bir sayı üretti: kaynağa giden sorgu, bayat okuma, isabet ve ıska,
indirilen bayt. Bu sayılar tek tek anlamlıydı ama hiçbiri "önbellek işe yarıyor mu" sorusunu
tek başına yanıtlamaz. Anahtar Tasarımı dersinde en yüksek isabet oranı yanlış gerçekleştirime
aitti; bu ders, doğru gerçekleştirimlerde bile tek bir oranın neyi gizlediğini gösterir.

Ölçütün tanımı basittir. **İsabet oranı**, isteklerin önbellekten karşılanan payıdır:

$$h = \frac{\text{isabet}}{\text{isabet} + \text{ıska}}$$

Ortalama yanıt süresi ise iki bileşenden oluşur: her istek önbelleğe bakar, ıska alanlar
ayrıca kaynağa gider. Önbellek erişimi $t_o$, kaynak erişimi $t_k$ ise:

$$T = t_o + (1 - h)\,t_k$$

Bu biçim, isabet oranındaki iyileşmenin neden orantısız kazanç verdiğini açıklar. Oran
%90'dan %95'e çıktığında iyileşme beş puandır, ama $1-h$ çarpanı 0,10'dan 0,05'e indiği için
kaynağa binen yük **yarıya** iner.

## Ölçüm Düzeneği

```sh
# kur.sh — olcum icin kitap ve odunc verisi
rm -f kutuphane.db
sqlite3 kutuphane.db <<'SQL'
CREATE TABLE kitap (kitap_id INTEGER PRIMARY KEY, baslik TEXT NOT NULL, yazar TEXT NOT NULL,
                    basim_yili INTEGER, sube_id INTEGER NOT NULL);
CREATE TABLE odunc (odunc_id INTEGER PRIMARY KEY, kitap_id INTEGER NOT NULL,
                    uye_id INTEGER NOT NULL, iade_tarihi TEXT);
INSERT INTO kitap SELECT n, 'Kitap ' || n, 'Yazar ' || (n % 40 + 1), 1950 + n % 70, n % 4 + 1
  FROM (WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n < 300) SELECT n FROM s);
INSERT INTO odunc SELECT n, n % 300 + 1, n % 200 + 1, CASE WHEN n % 5 = 0 THEN NULL ELSE '2025-12-31' END
  FROM (WITH RECURSIVE s(n) AS (SELECT 1 UNION ALL SELECT n+1 FROM s WHERE n < 4000) SELECT n FROM s);
CREATE INDEX odunc_uye ON odunc(uye_id);
SQL
sqlite3 kutuphane.db "SELECT 'kitap=' || (SELECT count(*) FROM kitap) || ' odunc=' || (SELECT count(*) FROM odunc);"
```

```
kitap=300 odunc=4000
```

Ölçüm için konu boyunca parça parça kurulan davranışları tek yerde toplayan bir önbellek
gerekir: yaşam süresi, kapasite dolduğunda **en son kullanılana göre tahliye** ve sayaçlar.

```js
// onbellek.mjs — sure sonu, en son kullanilana gore tahliye ve sayaclarla onbellek
export function onbellekAc({ kapasite = 128, omur = Infinity, saat = () => Date.now() } = {}) {
  const kutu = new Map();
  const sayac = { isabet: 0, iska: 0, tahliye: 0, sureSonu: 0, isabetBayt: 0, iskaBayt: 0 };
  const boyutu = (d) => Buffer.byteLength(JSON.stringify(d));
  return {
    sayac,
    al(anahtar) {
      const girdi = kutu.get(anahtar);
      if (girdi === undefined) { sayac.iska += 1; return undefined; }
      if (girdi.sonGecerlilik <= saat()) {
        kutu.delete(anahtar); sayac.sureSonu += 1; sayac.iska += 1; return undefined;
      }
      kutu.delete(anahtar); kutu.set(anahtar, girdi);   // en son kullanilan sona tasinir
      sayac.isabet += 1; sayac.isabetBayt += girdi.boyut;
      return girdi.deger;
    },
    koy(anahtar, deger) {
      const boyut = boyutu(deger);
      sayac.iskaBayt += boyut;
      kutu.delete(anahtar);
      kutu.set(anahtar, { deger, boyut, sonGecerlilik: saat() + omur });
      while (kutu.size > kapasite) { kutu.delete(kutu.keys().next().value); sayac.tahliye += 1; }
    },
    get girdiSayisi() { return kutu.size; },
    isabetOrani: () => sayac.isabet / (sayac.isabet + sayac.iska),
    baytIsabetOrani: () => sayac.isabetBayt / (sayac.isabetBayt + sayac.iskaBayt),
  };
}
```

Tahliye sırası `Map`'in ekleme sırasından gelir: okunan girdi silinip yeniden konarak sona
taşınır, kapasite aşıldığında baştaki — yani en uzun süredir kullanılmayan — girdi düşer.

## Kapasite ile İsabet Oranı

İlk ölçüm tek bir düğmeyi çevirir: önbellek kapasitesi. İş yükü 2000 istektir ve üç işlem
türünü kapsar; farklı anahtar sayıları farklı tekrar oranları üretir.

```js
// kapasite.mjs — kapasite arttikca isabet orani ve kaynaga giden sorgu nasil degisiyor
import { DatabaseSync } from "node:sqlite";
import { onbellekAc } from "./onbellek.mjs";

const db = new DatabaseSync("kutuphane.db");
const sorgu = { sayi: 0 };
const kaynak = {
  kitap: (id) => { sorgu.sayi += 1; return db.prepare(
    "SELECT kitap_id, baslik, yazar, basim_yili FROM kitap WHERE kitap_id = ?").get(id); },
  stok: (id) => { sorgu.sayi += 1; return db.prepare(
    `SELECT ? AS sube_id, count(*) AS raftaki FROM kitap k WHERE k.sube_id = ? AND NOT EXISTS
     (SELECT 1 FROM odunc o WHERE o.kitap_id = k.kitap_id AND o.iade_tarihi IS NULL)`).get(id, id); },
  liste: (id) => { sorgu.sayi += 1; return db.prepare(
    "SELECT kitap_id, baslik FROM kitap WHERE sube_id = ? ORDER BY kitap_id").all(id); },
};

function isYuku(adet) {                             // kitap istekleri 60 baslikta yogunlasir
  let tohum = 20250729;
  const rast = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648);
  return Array.from({ length: adet }, () => {
    const r = rast();
    if (r < 0.75) return ["kitap", Math.floor(rast() * 60) + 1];
    if (r < 0.9) return ["stok", Math.floor(rast() * 4) + 1];
    return ["liste", Math.floor(rast() * 4) + 1];
  });
}

const istekler = isYuku(2000);
console.log(["kapasite", "isabet", "iska", "isabet orani", "tahliye", "kaynak sorgusu", "sure~"]
  .map((b, i) => (i === 0 ? b.padEnd(10) : b.padStart([0, 8, 8, 14, 9, 16, 9][i]))).join(""));

for (const kapasite of [4, 8, 16, 32, 64, 128]) {
  sorgu.sayi = 0;
  const onbellek = onbellekAc({ kapasite });
  const t = performance.now();
  for (const [tur, id] of istekler) {
    const anahtar = `kutuphane:b1:n1:k${tur === "kitap" ? "genel" : `sube-${id}`}:${tur}:${id}`;
    let deger = onbellek.al(anahtar);
    if (deger === undefined) { deger = kaynak[tur](id); onbellek.koy(anahtar, deger); }
  }
  const sure = Math.round((performance.now() - t) / 10) * 10;
  const s = onbellek.sayac;
  console.log(String(kapasite).padEnd(10) + [String(s.isabet).padStart(8), String(s.iska).padStart(8),
    (`%` + (onbellek.isabetOrani() * 100).toFixed(1)).padStart(14), String(s.tahliye).padStart(9),
    String(sorgu.sayi).padStart(16), (sure + " ms").padStart(9)].join(""));
}
db.close();
```

```
kapasite    isabet    iska  isabet orani  tahliye  kaynak sorgusu    sure~
4              133    1867          %6.7     1863            1867   910 ms
8              289    1711         %14.4     1703            1711   760 ms
16             588    1412         %29.4     1396            1412   460 ms
32            1053     947         %52.6      915             947   200 ms
64            1851     149         %92.5       85             149    10 ms
128           1932      68         %96.6        0              68    10 ms
```

Süre sütunu on milisaniyeye yuvarlanmıştır ve makineye bağlıdır; ilk beş sütun değildir.

Eğrinin biçimi anlatıcıdır. Kapasite 32'den 64'e çıktığında isabet oranı %52,6'dan %92,5'e
sıçrıyor, 64'ten 128'e çıktığında yalnız dört puan artıyor. Sıçramanın yeri iş yükünün
**çalışma kümesini** verir: 60 kitap, 4 stok ve 4 liste anahtarı, toplam 68 farklı anahtar.
Kapasite bu sayının altındayken her yeni anahtar bir başkasını atar ve tahliye sayısı
ıskaların neredeyse tamamına eşit olur — kapasite 4'te 1867 ıskaya karşılık 1863 tahliye.
Kapasite çalışma kümesini geçtiğinde tahliye sıfırlanır ve kalan 68 ıska yalnız ilk
yüklemelerdir.

Bu, kapasite kararının doğru sorusunu verir: "ne kadar bellek ayıralım" değil, "çalışma kümesi
kaç anahtar". Tahliye sayacı bu soruyu doğrudan yanıtlar. Tahliye ıska sayısına yakınsa
önbellek kendi girdilerini birbirine yedirmektedir; sıfıra yakınsa kapasite yeterlidir.

Süre sütunu formülün doğruladığı şeyi gösteriyor. Kapasite 4'te isabet oranı %6,7 ve iş yükü
910 milisaniye sürdü; %92,5'te 10 milisaniyeye indi. Aradaki fark, ıska cezasının bu iş
yükünde büyük olmasından gelir — stok sorgusu diğerlerinden pahalıdır.

## Toplam Oranın Gizlediği

Yukarıdaki tabloda tek bir oran vardı çünkü iş yükünün her parçası önbelleğe uygundu.
Gerçek uygulamalarda böyle olmaz. Aşağıdaki iş yüküne, iki yüz farklı üyenin ödünç geçmişi
eklenmiştir: çok anahtarlı, az tekrarlı bir okuma.

```js
// ayrisik.mjs — tek bir isabet orani neyi gizler: ad alanina ve bayta gore ayrisim
import { DatabaseSync } from "node:sqlite";
import { onbellekAc } from "./onbellek.mjs";

const db = new DatabaseSync("kutuphane.db");
const kaynak = {
  kitap: (id) => db.prepare("SELECT kitap_id, baslik, yazar FROM kitap WHERE kitap_id = ?").get(id),
  stok: (id) => db.prepare(
    `SELECT ? AS sube_id, count(*) AS raftaki FROM kitap k WHERE k.sube_id = ? AND NOT EXISTS
     (SELECT 1 FROM odunc o WHERE o.kitap_id = k.kitap_id AND o.iade_tarihi IS NULL)`).get(id, id),
  liste: (id) => db.prepare("SELECT kitap_id, baslik FROM kitap WHERE sube_id = ? ORDER BY kitap_id").all(id),
  gecmis: (id) => db.prepare(
    "SELECT odunc_id, kitap_id FROM odunc WHERE uye_id = ? ORDER BY odunc_id").all(id),
};

let tohum = 20250729;
const rast = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648);
const istekler = Array.from({ length: 2000 }, () => {
  const r = rast();
  if (r < 0.70) return ["kitap", Math.floor(rast() * 60) + 1];
  if (r < 0.80) return ["stok", Math.floor(rast() * 4) + 1];
  if (r < 0.85) return ["liste", Math.floor(rast() * 4) + 1];
  return ["gecmis", Math.floor(rast() * 200) + 1];    // 200 uye, tekrar orani dusuk
});

function kosu(onbelleklenir) {
  const onbellek = onbellekAc({ kapasite: 64 });
  const tur = { kitap: [0, 0], stok: [0, 0], liste: [0, 0], gecmis: [0, 0] };  // [isabet, iska]
  let kaynakSorgusu = 0;
  for (const [t, id] of istekler) {
    if (!onbelleklenir.has(t)) { kaynak[t](id); kaynakSorgusu += 1; tur[t][1] += 1; continue; }
    const anahtar = `kutuphane:b1:n1:k${t === "kitap" ? "genel" : `sube-${(id % 4) + 1}`}:${t}:${id}`;
    const oncekiIsabet = onbellek.sayac.isabet;
    let deger = onbellek.al(anahtar);
    if (deger === undefined) { deger = kaynak[t](id); kaynakSorgusu += 1; onbellek.koy(anahtar, deger); }
    tur[t][onbellek.sayac.isabet > oncekiIsabet ? 0 : 1] += 1;
  }
  return { onbellek, tur, kaynakSorgusu };
}

const { onbellek, tur, kaynakSorgusu } = kosu(new Set(["kitap", "stok", "liste", "gecmis"]));

console.log(["ad alani", "istek", "isabet", "iska", "isabet orani"]
  .map((b, i) => (i === 0 ? b.padEnd(10) : b.padStart([0, 8, 8, 8, 14][i]))).join(""));
for (const [ad, [i, k]] of Object.entries(tur)) {
  console.log(ad.padEnd(10) + [String(i + k).padStart(8), String(i).padStart(8), String(k).padStart(8),
    ("%" + ((i / (i + k)) * 100).toFixed(1)).padStart(14)].join(""));
}
const s = onbellek.sayac;
console.log(`\ntoplam istek isabet orani : %${(onbellek.isabetOrani() * 100).toFixed(1)}`);
console.log(`bayt isabet orani         : %${(onbellek.baytIsabetOrani() * 100).toFixed(1)}`);
console.log(`isabetten donen bayt      : ${s.isabetBayt}   kaynaktan gelen bayt: ${s.iskaBayt}`);
console.log(`kaynak sorgusu            : ${kaynakSorgusu}`);

const ikinci = kosu(new Set(["kitap", "stok", "liste"]));    // gecmis hic onbelleklenmiyor
console.log(`\ngecmis onbelleklenmediginde:`);
console.log(`  kitap isabet orani      : %${((ikinci.tur.kitap[0] / (ikinci.tur.kitap[0] + ikinci.tur.kitap[1])) * 100).toFixed(1)}`);
console.log(`  tahliye                 : ${ikinci.onbellek.sayac.tahliye} (onceki: ${s.tahliye})`);
console.log(`  kaynak sorgusu          : ${ikinci.kaynakSorgusu}`);
db.close();
```

```
ad alani     istek  isabet    iska  isabet orani
kitap         1380     965     415         %69.9
stok           202     189      13         %93.6
liste          110      84      26         %76.4
gecmis         308      19     289          %6.2

toplam istek isabet orani : %62.8
bayt isabet orani         : %51.8
isabetten donen bayt      : 303731   kaynaktan gelen bayt: 282500
kaynak sorgusu            : 743

gecmis onbelleklenmediginde:
  kitap isabet orani      : %90.7
  tahliye                 : 77 (onceki: 679)
  kaynak sorgusu          : 449
```

Toplam oran %62,8'dir ve tek başına raporlandığında "orta düzeyde bir önbellek" izlenimi
verir. Ayrışım başka bir şey söylüyor: ödünç geçmişi ad alanının isabet oranı %6,2. İki yüz
farklı anahtar ve düşük tekrar, bu veriyi önbelleğe uygunsuz kılıyor. Dahası bu ad alanı
zararlıdır — koyduğu her girdi kapasiteden yer alır ve tekrar okunmadan tahliye edilir. Kitap
ad alanının isabet oranını %69,9'a düşüren şey budur.

Son bölüm bunu kanıtlıyor. Ödünç geçmişi hiç önbelleklenmediğinde kitap ad alanının isabet
oranı %69,9'dan %90,7'ye çıktı, tahliye sayısı 679'dan 77'ye indi ve kaynağa giden toplam
sorgu 743'ten 449'a düştü. Bir ad alanının önbellekten **çıkarılması** kaynak yükünü
düşürdü. Bu sonuç yalnız ayrışık ölçümle bulunabilirdi; toplam oran her iki koşuda da benzer
görünürdü.

Bundan çıkan işletme kuralı şudur: isabet oranı ad alanı başına ölçülür. Anahtar Tasarımı
dersinde anahtara konan ad alanı bölümünün ikinci işlevi budur — ölçütü ayrıştırılabilir
kılmak.

## Bayt İsabet Oranı

Çıktının ikinci satır çifti ayrı bir ayrımı gösteriyor. **İstek isabet oranı** %62,8,
**bayt isabet oranı** %51,8. Fark, değerlerin boyutlarının eşit olmamasından gelir: liste ve
geçmiş yanıtları kitap kaydından çok daha büyüktür.

İki oran farklı soruları yanıtlar. İstek isabet oranı kaynağa kaç çağrı gitmediğini söyler;
gidiş-dönüş ve sorgu maliyetini bu sayı belirler. Bayt isabet oranı ağdan geçmeyen veri
miktarını söyler; bant genişliği ve aktarım süresini bu sayı belirler. Frontend Kalitesi
kursundaki Statik Barındırma dersinde kenar önbelleği için tanımlanan bu iki ölçüt, sunucu
önbelleğinde de aynı ayrımı taşır.

Hangisinin izleneceği darboğaza bağlıdır. Kaynak sorgu sayısıyla zorlanıyorsa istek isabet
oranı, ağ ile zorlanıyorsa bayt isabet oranı belirleyicidir.

## Özet

- İsabet oranı isteklerin önbellekten karşılanan payıdır; ortalama süre $T = t_o + (1-h)t_k$
  biçiminde davranır, bu yüzden %90'dan %95'e çıkmak kaynak yükünü yarıya indirir.
- Kapasite çalışma kümesinin altındayken tahliye sayısı ıska sayısına yaklaşır; 68 anahtarlık
  kümede kapasite 32'den 64'e çıkınca isabet oranı %52,6'dan %92,5'e sıçradı.
- Tahliye sayacı kapasite kararının doğrudan ölçütüdür: ıskaya yakınsa önbellek kendi
  girdilerini yiyordur, sıfıra yakınsa kapasite yeterlidir.
- Tek bir toplam oran ad alanı farklarını gizler: %62,8'lik toplamın içinde %6,2'lik bir ad
  alanı vardı ve o ad alanı önbellekten çıkarıldığında kaynak sorgusu 743'ten 449'a indi.
- İstek isabet oranı kaynağa giden çağrı sayısını, bayt isabet oranı ağdan geçmeyen veri
  miktarını ölçer; aynı koşuda %62,8 ve %51,8 çıktılar.

## Sonraki Adım

Bu konu boyunca yapılan iş tek bir cümleyle özetlenebilir: aynı yanıtın yeniden üretilmesi
önlendi. Kapasite ölçümündeki 1867 kaynak sorgusu 68'e indi; okuma yolu gerçekten kısaldı.

Kısalmayan bir yol kaldı. Önbellek yalnız **okumaya** yarar; yazma her zaman kaynağa gider ve
isteğin süresine eklenir. Yazmanın yanına iliştirilmiş işler de öyledir: ödünç verildiğinde
gönderilen bildirim, gün sonunda üretilen gecikme raporu, yüklenen kapak görselinin
işlenmesi. Bunların hiçbiri kullanıcının yanıtı için gerekli değildir, ama hepsi yanıtın
gelmesini geciktirir. İstek yolunu kısaltmanın ikinci yolu işi hızlandırmak değil, işi
istekten **çıkarmaktır**. Sonraki ders bu yolu açar: bir işin eşzamansız yürütülmesinin ne
zaman doğru olduğunu, isteğin süresinden neyin düştüğünü ve karşılığında hangi yeni
sorumlulukların doğduğunu ele alır.
