---
title: 'Sorgu ve Toplulaştırma'
source: 'https://academia.sh/tr/kurslar/devops-gozlemlenebilirlik/sorgu-ve-toplulastirma'
course: 'Gözlemlenebilirlik ve İşletim'
language: tr
updated: '2026-08-17T18:09:06+00:00'
license: 'CC BY-SA 4.0'
---

# Sorgu ve Toplulaştırma

Aynı soru dört toplulaştırmayla yanıtlanır ve yanıtın değiştiği gösterilir: ortalama gizler, yüzdelik gösterir, toplam yanıltır, sayı ile oran farklı servisi işaret eder. Önceden toplulaştırmanın geri getirilemez biçimde kaybettiği ayrıntı ile kazandırdığı sorgu maliyeti aynı tabloda sayılır.

Bir önceki ders seri kümesinin hangi ayrımı sakladığını ölçtü. Ama hiçbir soru serinin kendisiyle
yanıtlanmaz: bir seri yüz binlerce sayıdır ve yanıt tek bir sayı olmak zorundadır. Araya
**toplulaştırma** girer — ortalama, yüzdelik, en büyük, toplam — ve bu ders o adımın kararsız
olduğunu gösterir: aynı veriden, aynı soruya, farklı toplulaştırmalarla **farklı yanıtlar** çıkar.

İkinci ölçü önceden toplulaştırmadır. Ham ölçümü saklamak yerine dakikalık özet saklamak sorgu
maliyetini iki mertebe düşürür ve karşılığında bir ayrıntıyı geri getirilemez biçimde siler.
Bu dersin ölçüsü, o iki tarafı aynı tabloda saymaktır.

Ölçüm yine **bölgesel ölçüm ağı** üzerindedir ve **kurgudur**; gerçek trafik koşturulmaz, sayılan
şey `node` ile üretilmiş bir **modeldir**.

**OG13.** Dört servisin istek hızı ve gecikme profili kurgudur ve önceden yazılıdır.
**OG14.** Ölçümler kendi üretecimizle üretilir, tohum `20260902`; bir saatlik pencerede 90.000
istek.
**OG15.** Faturalama servisinin isteklerinin yüzde altısı kilit bekler ve 950–1100 ms ek gecikme
alır; öteki üç servisin dağılımı düzgündür. Doğrulayıcıya ayrıca 90 saniye süren kurgu bir
bozulma eklenir (+900 ms) ve olaysız profil değerleri eşik tabanı sayılır.
**OG16.** Önceden toplulaştırma dakika başına dört alan tutar: adet, toplam, en büyük ve
dakikanın p95 değeri.
**OG17.** Özetten hesaplanan genel p95, dakika p95'lerinin istek sayısıyla ağırlıklı ortalamasıdır
— yüzdeliklerin birleştirilebileceği varsayımı bilerek uygulanır.
**OG18.** Süre ölçümü ortama bağlıdır; çıktıya yalnız bant denetiminin sonucu basılır, ham ölçüm
basılmaz.

## Ham Ölçüm Kümesi

```js
// olcum.mjs — bir saatlik istek olcumleri kendi uretecimizle uretilir; profiller kurgudur.
export const uretec = (t) => () => (t = (t * 1103515245 + 12345) % 2147483648) / 2147483648;
export const PENCERE = 3600;
export const PROFIL = {                              // servis: [istek/sn, taban, yayilim, kuyruk payi, kuyruk ms, hata orani]
  "okuma-toplayici": [12, 150, 110, 0, 0, 0.012],
  dogrulayici: [8, 90, 40, 0, 0, 0.006],
  faturalama: [3, 40, 30, 0.06, 950, 0.035],
  "is-emri": [2, 200, 40, 0, 0, 0.009],
};
export const olcumler = (tohum = 20260902) => {
  const rast = uretec(tohum), o = {};
  for (const [servis, [hiz, taban, yayilim, pay, kuyruk, hata]] of Object.entries(PROFIL)) {
    const d = [];
    for (let sn = 0; sn < PENCERE; sn++)
      for (let i = 0; i < hiz; i++) {
        const kuyruklu = rast() < pay;
        d.push({ sn, ms: Math.round(taban + rast() * yayilim + (kuyruklu ? kuyruk + rast() * 150 : 0)),
          hata: rast() < hata });
      }
    o[servis] = d;
  }
  return o;
};
export const yuzdelik = (a, p) => {
  const s = [...a].sort((x, y) => x - y);
  return s[Math.min(s.length - 1, Math.ceil(p * s.length) - 1)];
};
export const enBuyukAd = (o) => Object.entries(o).sort((a, b) => b[1] - a[1])[0][0];
```

Profiller birbirinden yalnız ortalamayla ayrılmaz. Okuma toplayıcı yüksek hacimli ve düzgün
dağılımlıdır; faturalama düşük hacimli ve kuyrukludur — isteklerinin yüzde altısı bir saniyenin
üstünde biter. Bu iki biçim, gerçek işletimde en sık karşılaşılan iki gecikme desenidir ve
aralarındaki farkı hangi toplulaştırmanın gördüğü baştan belli değildir.

## Aynı Soru, Dört Toplulaştırma

```js
// sorgu.mjs — ayni soru farkli toplulastirmalarla yanitlanir; onceden toplulastirmanin bedeli sayilir.
import { PROFIL, PENCERE, olcumler, yuzdelik, enBuyukAd } from "./olcum.mjs";

const ham = olcumler();
const servisler = Object.keys(PROFIL);
const toplamIstek = servisler.reduce((t, s) => t + ham[s].length, 0);
console.log(`bir saatlik olcum (kurgu model): ${toplamIstek.toLocaleString("tr-TR")} istek, ${servisler.length} servis`);
console.log("\nS1 'hangi servis yavasladi' — toplulastirmaya gore yanit");
console.log("toplulastirma   okuma-toplayici  dogrulayici  faturalama  is-emri   yanit");
const OLCU = [["ortalama ms", (d) => d.reduce((t, x) => t + x.ms, 0) / d.length],
  ["p95 ms", (d) => yuzdelik(d.map((x) => x.ms), 0.95)],
  ["en buyuk ms", (d) => Math.max(...d.map((x) => x.ms))],
  ["toplam sn", (d) => d.reduce((t, x) => t + x.ms, 0) / 1000]];
for (const [ad, f] of OLCU) {
  const v = Object.fromEntries(servisler.map((s) => [s, f(ham[s])]));
  console.log(`${ad.padEnd(14)}` + servisler.map((s, i) =>
    v[s].toFixed(0).padStart([17, 13, 12, 9][i])).join("") + `   ${enBuyukAd(v)}`);
}

console.log("\nS10 'hangi hata kodu artti' — sayi mi oran mi");
const hataSayi = Object.fromEntries(servisler.map((s) => [s, ham[s].filter((x) => x.hata).length]));
const hataOran = Object.fromEntries(servisler.map((s) => [s, 100 * hataSayi[s] / ham[s].length]));
console.log("olcu            " + servisler.map((s, i) => s.padStart([17, 13, 12, 9][i])).join("") + "   yanit");
console.log("hata sayisi   " + servisler.map((s, i) => String(hataSayi[s]).padStart([19, 13, 12, 9][i])).join("") +
  `   ${enBuyukAd(hataSayi)}`);
console.log("hata orani %  " + servisler.map((s, i) => hataOran[s].toFixed(2).padStart([19, 13, 12, 9][i])).join("") +
  `   ${enBuyukAd(hataOran)}`);
```

```
bir saatlik olcum (kurgu model): 90.000 istek, 4 servis

S1 'hangi servis yavasladi' — toplulastirmaya gore yanit
toplulastirma   okuma-toplayici  dogrulayici  faturalama  is-emri   yanit
ortalama ms                 204          110         125      220   is-emri
p95 ms                      255          128        1042      238   faturalama
en buyuk ms                 260          130        1166      240   faturalama
toplam sn                  8834         3162        1350     1582   okuma-toplayici

S10 'hangi hata kodu artti' — sayi mi oran mi
olcu              okuma-toplayici  dogrulayici  faturalama  is-emri   yanit
hata sayisi                   523          168         428       66   okuma-toplayici
hata orani %                 1.21         0.58        3.96     0.92   faturalama
```

Tek bir soru, dört farklı yanıt üretecek üç ayrı toplulaştırma. Sayılar aynı 90.000 isteğe aittir
ve hiçbiri yanlış değildir.

**Ortalama gizler.** Faturalamanın ortalaması 125 milisaniyedir ve dört servisin en düşük
ikincisidir. Kuyrukta bekleyen yüzde altılık dilim ortalamayı yalnız yetmiş milisaniye yukarı
çeker, çünkü ortalama uç değerleri hacme böler. Ortalamaya bakan bir gösterge faturalamayı hiç
göstermez.

**Yüzdelik gösterir.** Aynı servisin p95 değeri 1042 milisaniyedir — ötekilerin dört katı. Kuyruk
p95'te saklanamaz, çünkü yüzde altılık dilim yüzde beşlik eşiğin üstündedir. Bir istekten yirmisi
saniyeyi aşıyorsa bu ancak yüzdelikte görünür.

**Toplam yanıltır.** Toplam süreye bakıldığında yanıt okuma toplayıcıdır: 8834 saniye. Ama bu
sayının söylediği şey gecikme değil, **hacimdir** — okuma toplayıcı saatte 43.200 istek alır,
faturalama 10.800. Toplam, hızı ölçmek isterken iş yükünü ölçer. Kapasite sorusunda doğru,
gecikme sorusunda yanıltıcıdır.

Hata tablosu aynı ayrımı ikinci kez kurar. Hata **sayısı** okuma toplayıcıyı işaret eder (523),
hata **oranı** faturalamayı (%3,96). İkisi de doğrudur ve iki farklı soruyu yanıtlar: "en çok
hangi servis hata üretiyor" ile "hangi servis en bozuk". Nöbetteki kişiye yanlış olanı
gösterildiğinde inceleme, hata oranı üç kat düşük olan servisin günlüklerinde başlar.

## Sorgu Penceresi Aynı Şeyi Yapar

Toplulaştırma yalnız hangi işlevin uygulandığı değil, **hangi aralığa** uygulandığıdır. Aynı
ortalama bir dakikaya ve bir saate uygulandığında farklı şeyler ölçer. Aşağıdaki blok doğrulayıcıya
kısa bir bozulma yerleştirir ve aynı iki toplulaştırmayı dört pencerede sorar.

```js
// pencere.mjs — sorgu penceresi degistiginde ayni soru baska yanit verir.
import { PROFIL, PENCERE, olcumler, yuzdelik } from "./olcum.mjs";

const ham = olcumler();
const OLAY = { servis: "dogrulayici", bas: 1500, sure: 90, ek: 900 };   // kurgu bozulma
for (const x of ham[OLAY.servis])
  if (x.sn >= OLAY.bas && x.sn < OLAY.bas + OLAY.sure) x.ms += OLAY.ek;
const d = ham[OLAY.servis];
const taban = { ort: 110, p95: 128 };                    // olaysiz profilin degerleri
console.log(`${OLAY.servis} servisinde ${OLAY.sure} sn suren +${OLAY.ek} ms bozulma` +
  ` (saatin %${(100 * OLAY.sure / PENCERE).toFixed(1)}'i); esik taban degerinin iki kati`);
console.log("sorgu penceresi  pencere  en yuksek ort ms  esigi asan  en yuksek p95 ms  esigi asan  fark etme");
for (const w of [60, 300, 900, 3600]) {
  const say = PENCERE / w, ort = [], p95 = [];
  for (let i = 0; i < say; i++) {
    const g = d.filter((x) => x.sn >= i * w && x.sn < (i + 1) * w).map((x) => x.ms);
    ort.push(g.reduce((a, b) => a + b, 0) / g.length); p95.push(yuzdelik(g, 0.95));
  }
  console.log(`${String(`${w / 60} dk`).padEnd(15)}${String(say).padStart(8)}` +
    `${Math.max(...ort).toFixed(0).padStart(18)}${String(`${ort.filter((v) => v > 2 * taban.ort).length}/${say}`).padStart(12)}` +
    `${String(Math.max(...p95)).padStart(18)}${String(`${p95.filter((v) => v > 2 * taban.p95).length}/${say}`).padStart(12)}` +
    `${String(`${w / 60} dk`).padStart(11)}`);
}
console.log(`\ncozunurluk basina kayit (bir saat, dort servis): ` +
  [60, 300, 900, 3600].map((w) => `${w / 60} dk ${4 * (PENCERE / w) * 4}`).join("   "));
```

```
dogrulayici servisinde 90 sn suren +900 ms bozulma (saatin %2.5'i); esik taban degerinin iki kati
sorgu penceresi  pencere  en yuksek ort ms  esigi asan  en yuksek p95 ms  esigi asan  fark etme
1 dk                 60              1009        2/60              1028        2/60       1 dk
5 dk                 12               380        1/12              1023        1/12       5 dk
15 dk                 4               200         0/4              1008         1/4      15 dk
60 dk                 1               132         0/1               129         0/1      60 dk

cozunurluk basina kayit (bir saat, dort servis): 1 dk 960   5 dk 192   15 dk 64   60 dk 16
```

Bozulma saatin yüzde iki buçuğudur ve dört pencerede dört farklı görünür. Bir dakikalık pencerede
hem ortalama hem yüzdelik bin milisaniyenin üstüne çıkar ve iki pencere eşiği aşar. Beş dakikada
ortalama 380'e iner ama hâlâ eşiğin üstündedir. **On beş dakikada ortalama ölür**: en yüksek değer
200 milisaniyedir ve iki yüz yirmilik eşiğin altında kalır — hiçbir pencere işaretlenmez. Aynı
pencerede yüzdelik hâlâ 1008 milisaniye görür ve bir pencere işaretler. Bir saatte ikisi de kaybolur:
ortalama 132, yüzdelik 129 milisaniye — olayın hiçbir izi yoktur.

Kayıp sayısı ile bedel aynı satırdadır. Bir dakikalık çözünürlük saatte 960, bir saatlik çözünürlük
16 kayıt tutar — altmış kat fark. Fark etme süresi de pencerenin kendisidir: bir pencere kapanmadan
sorgulanamaz, dolayısıyla on beş dakikalık çözünürlük en iyi durumda on beş dakikalık bir gecikme
demektir. Bozulma doksan saniye sürüyorsa o on beş dakika olayın tamamının bitmesinden sonrasına
düşer.

## Önceden Toplulaştırmanın İki Yüzü

Ham ölçümü istek başına saklamak pahalıdır. Yaygın karşılık dakikalık özet tutmaktır: her dakika
için adet, toplam, en büyük ve dakikanın p95 değeri. Aşağıdaki blok özeti gerçekten üretir ve
aynı soruları iki depoya sorar.

```js
// onagg.mjs — onceden toplulastirmanin kaybettigi ayrinti ve kazandirdigi sorgu maliyeti.
import { PROFIL, olcumler, yuzdelik } from "./olcum.mjs";

const ham = olcumler(), servisler = Object.keys(PROFIL);
const ozet = {};                                     // dakikalik ozet: adet, toplam, en buyuk, dakika p95
for (const s of servisler) {
  ozet[s] = Array.from({ length: 60 }, (_, d) => {
    const g = ham[s].filter((x) => Math.floor(x.sn / 60) === d).map((x) => x.ms);
    return { adet: g.length, toplam: g.reduce((a, b) => a + b, 0), enBuyuk: Math.max(...g), p95: yuzdelik(g, 0.95) };
  });
}
const hamHepsi = servisler.flatMap((s) => ham[s].map((x) => x.ms));
const hamKayit = hamHepsi.length, ozetKayit = servisler.length * 60 * 4;
const hamP95 = yuzdelik(hamHepsi, 0.95);
const ozetP95 = servisler.reduce((t, s) => t + ozet[s].reduce((a, d) => a + d.p95 * d.adet, 0), 0) / hamKayit;
const hamOrt = hamHepsi.reduce((a, b) => a + b, 0) / hamKayit;
const ozetOrt = servisler.reduce((t, s) => t + ozet[s].reduce((a, d) => a + d.toplam, 0), 0) / hamKayit;
const sapma = (o, h) => `%${(100 * Math.abs(o - h) / h).toFixed(1)}`;

console.log(`ham ${hamKayit.toLocaleString("tr-TR")} kayit, dakikalik ozet ${ozetKayit} kayit` +
  ` (${(hamKayit / ozetKayit).toFixed(0)} kat kucuk)`);
console.log("\nsoru                        ham yanit    ozetten yanit   sapma");
const SATIR = [["genel ortalama ms", hamOrt.toFixed(1), ozetOrt.toFixed(1), sapma(ozetOrt, hamOrt)],
  ["genel p95 ms", String(hamP95), ozetP95.toFixed(1), sapma(ozetP95, hamP95)],
  ["en buyuk ms", String(Math.max(...hamHepsi)),
    String(Math.max(...servisler.flatMap((s) => ozet[s].map((d) => d.enBuyuk)))), "%0.0"],
  ["1 sn ustu istek", String(hamHepsi.filter((x) => x > 1000).length), "hesaplanamaz", "-"]];
for (const [a, h, o, s] of SATIR)
  console.log(`${a.padEnd(26)}${h.padStart(11)}${o.padStart(16)}${s.padStart(8)}`);

// sorgu maliyeti: ayni p95 sorgusu iki depoda TEKRAR kez kosturulur
const TEKRAR = 40;
const beklenenOran = hamKayit / ozetKayit;
const olc = (f) => { const b = process.hrtime.bigint(); for (let i = 0; i < TEKRAR; i++) f(); return Number(process.hrtime.bigint() - b); };
const hamSure = olc(() => servisler.map((s) => yuzdelik(ham[s].map((x) => x.ms), 0.95)));
const ozetSure = olc(() => servisler.map((s) => yuzdelik(ozet[s].map((d) => d.p95), 0.95)));
const olculenOran = hamSure / ozetSure;
console.log(`\np95 sorgusu ${TEKRAR} kez kosturuldu`);
console.log(`  taranan kayit          ham ${(hamKayit * TEKRAR).toLocaleString("tr-TR")}` +
  `   ozet ${(ozetKayit * TEKRAR).toLocaleString("tr-TR")}   beklenen oran ${beklenenOran.toFixed(0)} kat`);
console.log(`  olculen sure orani 5 ile 20000 kat arasinda mi: ${olculenOran > 5 && olculenOran < 20000 ? "evet" : "hayir"}`);
```

```
ham 90.000 kayit, dakikalik ozet 960 kayit (94 kat kucuk)

soru                        ham yanit    ozetten yanit   sapma
genel ortalama ms               165.9           165.9    %0.0
genel p95 ms                      251           288.1   %14.8
en buyuk ms                      1166            1166    %0.0
1 sn ustu istek                   729    hesaplanamaz       -

p95 sorgusu 40 kez kosturuldu
  taranan kayit          ham 3.600.000   ozet 38.400   beklenen oran 94 kat
  olculen sure orani 5 ile 20000 kat arasinda mi: evet
```

Tablo üç ayrı davranışı yan yana koyar. Ortalama **birebir korunur**: sapma sıfırdır, çünkü
ortalama toplam ve adetten yeniden kurulabilir — dakikalık toplamların toplamı saatin toplamıdır.
En büyük değer de korunur, aynı nedenle: en büyüklerin en büyüğü gerçek en büyüktür.

Yüzdelik korunmaz. Genel p95 hamda 251, özetten 288 milisaniyedir; sapma yüzde 14,8. Nedeni tek
cümledir: **yüzdeliklerin ortalaması yüzdelik değildir.** Dakikalık p95 değerleri her dakikanın
kendi dağılımının üst dilimidir; bunları istek sayısıyla ağırlıklandırıp toplamak, saatin
dağılımının üst dilimini vermez. Yön de rastlantı değildir — bu veri kümesinde özet yukarı sapar,
çünkü faturalamanın kuyruğu her dakikanın p95'ini yukarı çeker ve o dakikalar toplamda seyrelmez.
Başka bir veri kümesinde aynı yöntem aşağı sapabilir; kestirilebilir olan tek şey sapmanın
varlığıdır.

Son satır tümüyle kayıptır. "Kaç istek bir saniyeyi aştı" sorusunun hamda yanıtı 729'dur; özette
yanıt yoktur ve yaklaşık bir yanıt da yoktur, çünkü eşiğin üstündeki istek sayısı özetin dört
alanının hiçbirinde durmaz. Ayrıntı silinmiştir ve geri getirilemez. Bunu görünür kılan şey de
yoktur: özet üzerinde bu sorgu yazıldığında depo hata vermez, yalnız sorgunun yazılamayacağı
anlaşılır.

Kazanç tarafı da aynı tablodadır. Aynı p95 sorgusu ham depoda 3,6 milyon kayıt tarar, özet depoda
38.400 — **94 kat** az. Süre ölçümü ortama bağlıdır ve buraya basılmaz; ölçülen oranın beklenen
mertebeyle uyumlu olup olmadığı bant denetiminden geçirilir ve sonuç `evet` yazılır.

Karar iki sayının çarpışmasındadır: doksan dört kat ucuz sorgu, yüzde on beş sapmış bir yüzdelik
ve tümüyle kaybolmuş bir eşik sorusu. Özet, ortalama ve en büyük soran gösterge tabloları için
bedavaya yakındır; yüzdelik ve eşik soran uyarı kuralları için yanlış bir tabandır.

## Özet

- Aynı soru dört toplulaştırmayla üç farklı yanıt verir: ortalama `is-emri`, p95 ve en büyük
  `faturalama`, toplam `okuma-toplayici`. Hiçbiri yanlış değildir; her biri başka bir şeyi ölçer.
- Ortalama uç dilimi hacme böldüğü için gizler (faturalama 125 ms), yüzdelik gösterir (1042 ms);
  toplam gecikme yerine hacmi ölçer.
- Hata sayısı ile hata oranı farklı servisi işaret eder: 523 hata `okuma-toplayici`, %3,96 oran
  `faturalama`.
- Sorgu penceresi de bir toplulaştırmadır: 90 saniyelik bir bozulma 1 dakikalık pencerede hem
  ortalamada hem yüzdelikte görünür, 15 dakikada yalnız yüzdelikte, 60 dakikada hiçbirinde.
  Çözünürlük 960 kayıttan 16 kayda inerken fark etme süresi 1 dakikadan 60 dakikaya çıkar.
- Önceden toplulaştırma ortalamayı ve en büyüğü birebir korur (%0,0), p95'i %14,8 saptırır,
  eşik üstü istek sayısını tümüyle siler — 729 istek özette hesaplanamaz.
- Aynı sorgu ham depoda 3,6 milyon, özet depoda 38.400 kayıt tarar: 94 kat ucuz. Kazanç ile kayıp
  aynı kararın iki yüzüdür.

## Sonraki Adım

Buraya kadar her yanıt bir sayıydı: kaç milisaniye, kaç istek, kaç hata. Ama tablodaki hiçbir sayı
**neden** sorusunu yanıtlamaz. Faturalamanın p95'inin 1042 milisaniye olduğu bilinir; hangi
kilidin, hangi sorgunun, hangi bölgenin okumasında beklendiği bilinmez. O bilgi ölçütte değil,
sürecin kendi yazdığı metinde durur. Bir sonraki ders günlük akışını ele alır ve o metnin toplama
hattından geçerken kaç satırının kaybolduğunu katman katman sayar.
