---
title: 'Sıcak Anahtar ve Sürü Etkisi'
source: 'https://academia.sh/tr/kurslar/eszamansiz-isleme/sicak-anahtar-ve-suru-etkisi'
course: 'Önbellekleme, Kuyruklar ve Eşzamansız İşleme'
language: tr
updated: '2026-08-17T18:06:47+00:00'
license: 'CC BY-SA 4.0'
---

# Sıcak Anahtar ve Sürü Etkisi

Sıcak bir anahtarın girdisi düştüğünde eşzamanlı yüz isteğin kaynağa yığılmasının ölçülmesi ve tek uçuş kilidiyle bunun tek üretime indirilmesi; yeniden doğrulayarak sunmanın bekleme süresini sıfırlaması, yaşam süresinin dağıtılmasıyla eşzamanlı süre sonunun önlenmesi ve sıcak anahtarın bölümlere yayılması.

Önceki derslerin bütün ölçümleri tek bir sırada yapıldı: bir istek ıska alır, kaynağa gider,
girdiyi koyar, sıradaki istek isabet alır. Bu sıra, isteklerin birbirini beklediğini
varsayar. Gerçek bir sunucuda varsaymaz.

Kütüphane sisteminin en çok okunan anahtarı Merkez şubesinin stok sayısıdır. O girdi
düştüğünde, düşme anıyla yeni girdinin konması arasında geçen sürede gelen **her** istek ıska
alır ve **her biri** kaynağa gider. Bu davranışa **sürü etkisi** (cache stampede) denir ve
sonucu şudur: kaynak, en çok korunduğu anahtarda en yüksek yükü, tam da önbelleğin boşaldığı
anda görür. Bu ders davranışı ölçer ve üç önlemi karşılaştırır.

## Yüz İsteğin Aynı Anda Gelmesi

Ölçüm düzeneğinde kaynak erişimi eşzamansız bir çağrıdır: sorgu ile gidiş-dönüş birlikte 30
milisaniye sürer. Sayılan şey süre değil, **kaynağa giden çağrı sayısıdır**.

```js
// suru.mjs — sicak anahtarin girdisi dustugu anda 100 istek birden geliyor
import { setTimeout as bekle } from "node:timers/promises";

const GECIKME = 30;                                // kaynak erisimi: sorgu + gidis-donus
const sayac = { kaynak: 0 };
async function kaynaktanUret(anahtar) {            // sube stok sayisi: pahali toplama sorgusu
  sayac.kaynak += 1;
  await bekle(GECIKME);
  return { anahtar, raftaki: 137 };
}

const ANAHTAR = "kutuphane:b1:n1:ksube-1:stok";
const ISTEK = 100;

async function kosu(ad, kur) {
  sayac.kaynak = 0;
  const olcu = { bekleyen: 0, bayat: 0 };
  const oku = kur(olcu);
  const t = performance.now();
  await Promise.all(Array.from({ length: ISTEK }, () => oku(ANAHTAR)));
  const sure = performance.now() - t;
  return `${ad.padEnd(28)}${String(sayac.kaynak).padStart(14)}${String(olcu.bekleyen).padStart(16)}` +
    `${String(olcu.bayat).padStart(12)}${(String(Math.round(sure / 10) * 10) + " ms").padStart(12)}`;
}

// 1) Duz yaninda okuma: girdi yok, herkes kaynaga gider.
const duz = (olcu) => {
  const kutu = new Map();
  return async (a) => {
    if (kutu.has(a)) return kutu.get(a);
    olcu.bekleyen += 1;
    const d = await kaynaktanUret(a);
    kutu.set(a, d);
    return d;
  };
};

// 2) Tek ucus: ayni anahtar icin tek uretim; digerleri ayni sozu bekler.
const tekUcus = (olcu) => {
  const kutu = new Map();
  const ucus = new Map();
  return async (a) => {
    if (kutu.has(a)) return kutu.get(a);
    olcu.bekleyen += 1;
    if (!ucus.has(a)) {
      ucus.set(a, kaynaktanUret(a).then((d) => { kutu.set(a, d); ucus.delete(a); return d; }));
    }
    return ucus.get(a);
  };
};

// 3) Yeniden dogrulayarak sunma: suresi dolmus girdi hemen donulur, yenileme arkada baslar.
const yenidenDogrulayarakSun = (olcu) => {
  const kutu = new Map([[ANAHTAR, { deger: { anahtar: ANAHTAR, raftaki: 131 }, taze: false }]]);
  const ucus = new Map();
  return async (a) => {
    const girdi = kutu.get(a);
    if (girdi && !girdi.taze && !ucus.has(a)) {     // yenilemeyi baslat, bekleme
      ucus.set(a, kaynaktanUret(a).then((d) => { kutu.set(a, { deger: d, taze: true }); ucus.delete(a); }));
    }
    if (girdi) { if (!girdi.taze) olcu.bayat += 1; return girdi.deger; }
    olcu.bekleyen += 1;
    return kaynaktanUret(a);
  };
};

console.log(["strateji", "kaynak cagrisi", "bekleyen istek", "bayat yanit", "sure~"]
  .map((b, i) => (i === 0 ? b.padEnd(28) : b.padStart([0, 14, 16, 12, 12][i]))).join(""));
console.log(await kosu("duz", duz));
console.log(await kosu("tek ucus", tekUcus));
console.log(await kosu("yeniden dogrulayarak sunma", yenidenDogrulayarakSun));
```

```
strateji                    kaynak cagrisi  bekleyen istek bayat yanit       sure~
duz                                    100             100           0       30 ms
tek ucus                                 1             100           0       30 ms
yeniden dogrulayarak sunma               1               0         100        0 ms
```

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

**Düz** kurulumda yüz istek yüz kaynak çağrısı üretti. Kod hatalı değil: her istek önbelleğe
baktı, bulamadı, kaynağa gitti. Kusur, ıska ile girdinin konması arasındaki boşlukta yatar.
Bu boşluk kaynak erişimi kadar sürer ve o süre boyunca gelen her istek aynı işi yeniden
başlatır. Yükün büyüklüğü isteklerin gelme hızıyla belirlenir: saniyede bin istek alan bir
uç noktada 30 milisaniyelik boşluk otuz eşzamanlı sorgu demektir.

**Tek uçuş** (single-flight) kilidi kaynak çağrısını bire indirdi. Kurulum basittir: üretim
başlatılmadan önce anahtar bir uçuş tablosuna yazılır ve sonraki istekler aynı sözü
bekler. Yüz isteğin hepsi yine bekledi — sonuçta veri henüz yoktu — ama kaynak tek sorgu
gördü. Bu, sürü etkisinin doğrudan çözümüdür.

**Yeniden doğrulayarak sunma** (stale-while-revalidate) farklı bir ödün verir. Süresi dolmuş
girdi silinmez; istek gelince eski değer hemen döndürülür ve yenileme arka planda başlatılır.
Bekleyen istek sayısı sıfıra indi, karşılığında yüz yanıt bayat veri taşıdı. Frontend
Kalitesi kursundaki Önbellek Stratejileri dersinde tarayıcı tarafında tanıtılan aynı kalıp,
sunucu tarafında da aynı ödünü verir: bekleme yerine bayatlık.

Üç satır arasındaki seçim veriye bağlıdır. Şube stok sayısı birkaç saniye eski olabilir;
orada yeniden doğrulayarak sunma en iyi seçimdir. Ödünç verme sırasında yapılan uygunluk
denetimi bayat veriyle çalışamaz; orada tek uçuş kullanılır.

Tek uçuş tablosunun kapsamı da ayrı bir karardır. Süreç içindeki tablo yalnız o örneği
korur; dört uygulama örneği çalışıyorsa kaynağa bir yerine dört çağrı gider. Kaynağı kesin
olarak tek çağrıya indirmek için kilidin paylaşılan depoda tutulması gerekir; bu, doğruluğu
artırırken her ıskaya bir ağ turu ekler.

## Süre Sonlarının Aynı Ana Düşmesi

Sürü etkisinin ikinci biçimi tek bir anahtarda değil, aynı anda yüklenmiş bir anahtar
kümesinde ortaya çıkar. Bir uygulama örneği yeniden başlatıldığında ya da bir nesil sayacı
artırıldığında yüzlerce anahtar aynı adımda önbelleğe girer. Hepsine aynı yaşam süresi
verilirse hepsi aynı adımda düşer.

```js
// sicak-anahtar.mjs — es zamanli sure sonu ve tek dugume yigilan sicak anahtar
import { createHash } from "node:crypto";

const ozet = (m) => parseInt(createHash("sha256").update(m).digest("hex").slice(0, 8), 16);

console.log("--- 200 anahtar ayni adimda yuklendi, omur=100");
for (const [ad, omur] of [["sabit omur", () => 100], ["dagitilmis omur", (a) => 100 + (ozet(a) % 40)]]) {
  const adimBasi = new Map();
  for (let i = 1; i <= 200; i++) {
    const anahtar = `kutuphane:b1:n1:ksube-1:kitap:${i}`;
    const adim = omur(anahtar);
    adimBasi.set(adim, (adimBasi.get(adim) ?? 0) + 1);
  }
  const enYuksek = Math.max(...adimBasi.values());
  console.log(`${ad.padEnd(18)} sure sonu adimi=${adimBasi.size}  tek adimdaki en yuksek iska=${enYuksek}`);
}

console.log("--- 4 bolumlu paylasilan onbellek, isteklerin %60'i tek anahtarda");
const SICAK = "kutuphane:b1:n1:ksube-1:stok";
const istekler = Array.from({ length: 1000 }, (_, i) =>
  i % 10 < 6 ? SICAK : `kutuphane:b1:n1:ksube-${(i % 3) + 2}:kitap:${i % 40}`);

function dagilim(donustur) {
  const bolum = [0, 0, 0, 0];
  for (const [i, a] of istekler.entries()) bolum[ozet(donustur(a, i)) % 4] += 1;
  return bolum;
}
const yaz = (ad, b) => console.log(`${ad.padEnd(22)}${b.map((n) => String(n).padStart(8)).join("")}` +
  `   en yuklu bolum=%${((Math.max(...b) / 1000) * 100).toFixed(0)}`);

yaz("tek anahtar", dagilim((a) => a));
yaz("4 kopyaya bolunmus", dagilim((a, i) => (a === SICAK ? `${a}#${i % 4}` : a)));
yaz("8 kopyaya bolunmus", dagilim((a, i) => (a === SICAK ? `${a}#${i % 8}` : a)));
```

```
--- 200 anahtar ayni adimda yuklendi, omur=100
sabit omur         sure sonu adimi=1  tek adimdaki en yuksek iska=200
dagitilmis omur    sure sonu adimi=40  tek adimdaki en yuksek iska=11
--- 4 bolumlu paylasilan onbellek, isteklerin %60'i tek anahtarda
tek anahtar                150     116      83     651   en yuklu bolum=%65
4 kopyaya bolunmus         300     416      83     201   en yuklu bolum=%42
8 kopyaya bolunmus         300     341     158     201   en yuklu bolum=%34
```

Sabit ömürde iki yüz anahtarın tamamı tek bir adımda düştü. Yaşam süresine anahtardan
türetilen küçük bir sapma eklendiğinde süre sonları kırk adıma yayıldı ve tek adımdaki en
yüksek ıska sayısı 11'e indi. Sapmanın anahtardan türetilmesi önemlidir: rastgele bir sayı
kullanılırsa aynı anahtar farklı örneklerde farklı sürelerde düşer ve davranış
yinelenemez hâle gelir.

## Sıcak Anahtarın Kendisi

Çıktının ikinci yarısı ayrı bir sorunu ölçüyor. Paylaşılan bir önbellek birden çok düğüme
bölündüğünde anahtar, özetine göre bir bölüme düşer. İsteklerin %60'ı tek bir anahtardaysa o
anahtarın bölümü isteklerin %65'ini görür; kalan üç bölüm boşta kalır. Bölüm sayısını
artırmak bu dağılımı düzeltmez, çünkü sorun anahtar sayısının azlığında değil, **tek bir
anahtarın ağırlığındadır**.

Çözüm anahtarı çoğaltmaktır: sıcak anahtar sabit sayıda kopyaya bölünür ve istek bunlardan
birine yönlendirilir. Dört kopyada en yüklü bölüm %65'ten %42'ye, sekiz kopyada %34'e indi.
Dağılım eşit değildir — özet işlevi kopyaları bölümlere eşit dağıtmaz — ama yığılma
kırılmıştır.

Çoğaltmanın bedeli iki tanedir. Aynı değer birden çok kez saklanır ve geçersizleştirme artık
tek anahtarı değil, bütün kopyaları düşürmelidir. İkincisi, kopya sayısı sabit olduğu için
önceki dersteki nesil sayacı burada da işe yarar: nesil artırıldığında bütün kopyalar tek
hamlede erişilemez olur.

Sıcak anahtarın üçüncü ve en ucuz önlemi, uygulama örneğinin içinde kısa ömürlü bir yerel
kopya tutmaktır. İlk derste ölçülen katman zincirinin sunucu halkası budur: paylaşılan
önbelleğin önünde duran birkaç saniyelik yerel bir girdi, sıcak anahtara giden ağ turlarının
çoğunu keser.

## Özet

- Sıcak bir anahtarın girdisi düştüğünde ıska ile girdinin konması arasındaki boşlukta gelen
  her istek kaynağa gider: yüz eşzamanlı istek yüz kaynak çağrısı üretti.
- Tek uçuş kilidi aynı anahtar için üretimi bire indirir; yüz istek yine bekledi ama kaynak
  tek sorgu gördü. Kilit süreç içindeyse koruma örnek sayısı kadar zayıflar.
- Yeniden doğrulayarak sunma bekleyen istek sayısını sıfıra indirir ve karşılığında bayat yanıt döndürür;
  seçim, verinin bayatlık toleransına bağlıdır.
- Aynı anda yüklenen iki yüz anahtar sabit ömürle tek adımda düştü; ömre anahtardan türetilen
  bir sapma eklendiğinde tek adımdaki en yüksek ıska 200'den 11'e indi.
- Bölümlere ayrılmış paylaşılan bir önbellekte tek sıcak anahtar bir bölüme %65 yük bindirdi;
  anahtar sekiz kopyaya bölündüğünde en yüklü bölüm %34'e indi.

## Sonraki Adım

Bu derse kadar bütün önbellekler uygulamanın içinde kuruldu ve bütün kararlar kodda verildi.
Yanıt istemciye gönderildiğinde ise önbellekleme kararı artık kodun elinde değildir: tarayıcı,
aradaki paylaşılan önbellekler ve kenar katmanı, yalnız yanıtın başlıklarında yazana bakar.
Bu, ilk derste "denetlenemez" diye anılan katmanla konuşmanın tek yoludur ve kendi
sözlüğü vardır — hangi yanıt saklanabilir, ne kadar taze sayılır, saklanan kopyanın hâlâ
geçerli olup olmadığı kaynağa sorulmadan nasıl anlaşılır. Sonraki ders bu sözlüğü bir HTTP
sunucusu ile gerçek istekler üzerinde kurar.
