---
title: 'Zaman Serisi Depoları'
source: 'https://academia.sh/tr/kurslar/nosql/zaman-serisi-depolari'
course: 'İlişkisel Olmayan Veri Modelleri'
language: tr
updated: '2026-08-17T18:08:58+00:00'
license: 'CC BY-SA 4.0'
---

# Zaman Serisi Depoları

Zaman damgalı ölçüm verisinin iki yerleşimde saklanmasının bedeli: satır başına tam damga ile bölüt başına taban damga, fark kodlaması ve blok sıkıştırmanın saklanan bayt farkı, aralık sorgusunun taradığı bölüt ve giriş sayısının aralık daraldıkça tersine dönmesi, saklama politikasının bölüt sınırına hizalandığında dokunulan girişi sıfıra indirmesi.

Buraya kadarki dört ailede kaydın kendisi bir varlıktı — bir kitap, bir üye, bir ödünç — ve en
son ele alınan model bağın kendisini birinci sınıf nesne yaptı. Kütüphanenin verisinin bir
bölümü ise ne varlıktır ne bağ: raf bölgelerinin sıcaklığı, turnikenin saydığı kişi, ödünç
sayacının değeri. Bunlar **zaman serisidir (time series)** — bir kaynağa ait, zaman damgalı,
sırayla gelen ölçümler.

Bu verinin yük profili öteki ailelerinkine benzemez. Yazma yalnız sona eklemedir; geçmiş bir
ölçüm neredeyse hiç güncellenmez. Okuma tekil kayda değil **aralığa** gelir. Ve verinin bir son
kullanma tarihi vardır: üç ay önceki dakikalık ölçüm hiçbir soruya girmez. Ders bu üç özelliğin
depo tarafındaki karşılığını ölçüyor: yerleşimin saklanan bayta etkisi, aralık sorgusunun
taradığı birim, saklama politikasının bedeli.

## İki Yerleşim, Aynı Ölçümler

Ölçüm satırı küçüktür: bir kaynak kimliği, bir zaman damgası ve tek bir sayı — damga, yanında
taşıdığı değerden büyük olabilir. Zaman serisi deposunun ilk kararı buradan gelir: ölçümler tek
tek değil, **bölütler (segment)** hâlinde saklanır; her bölüt bir kaynağın bir zaman kutusudur,
**taban damgasını** bir kez yazar, geri kalanı fark olarak tutar.

**NS25:** on iki raf bölgesinde birer sıcaklık ölçeri vardır, her biri 60 saniyede bir ölçüm
yazar ve otuz günlük veri incelenir. **NS26:** satır yerleşiminde bir ölçüm 2 bayt kaynak
kimliği, 8 bayt zaman damgası ve 8 bayt değer taşır. **NS6:** bölüt birimi bir kaynak–gün
çiftidir.

```js
// zaman/yerlesim.mjs — ayni olcum dizisi iki yerlesimde seriliyor: (a) satir basina tam
// zaman damgasi + deger, (b) bolut basina taban damga + fark kodlamasi + blok sikistirma.
// Sayilan sey saklanan bayttir; sikistirma gercekten calistirilir, tohum gorunurdur.
import { deflateSync } from "node:zlib";
const SERI = 12, GUN = 30, ARALIK = 60_000, T0 = Date.UTC(2024, 5, 1);
const NOKTA = 86_400_000 / ARALIK;               // bolut basina giris: gunluk olcum sayisi
let tohum = 20240601;                            // gorunur tohum; uretec kendi yazildi
const gurultu = () => ((tohum = (tohum * 1103515245 + 12345) % 2147483648) / 2147483648 - 0.5);
const olcum = (s, i) =>                          // ondalik derece cinsinden tamsayi: 18,4 -> 184
  Math.round((18 + s * 0.25 + 3 * Math.sin((i / NOKTA) * 2 * Math.PI) + gurultu() * 0.6) * 10);
const varint = (n) => { const b = []; let x = ((n << 1) ^ (n >> 31)) >>> 0;   // zigzag + varint
  do { let y = x & 0x7f; x >>>= 7; if (x) y |= 0x80; b.push(y); } while (x); return b; };

const satir = Buffer.alloc(SERI * GUN * NOKTA * 18);       // 2 bayt seri + 8 damga + 8 deger
let ofset = 0, hamBayt = 0, bolutBayt = 0, bolutSayisi = 0;
for (let s = 0; s < SERI; s += 1) for (let g = 0; g < GUN; g += 1) {
  const govde = [];                              // bolut govdesi: farklarin varinti
  let oncekiFark = 0, oncekiDeger = null;
  for (let i = 0; i < NOKTA; i += 1) {
    const t = T0 + g * 86_400_000 + i * ARALIK, d = olcum(s, g * NOKTA + i);
    satir.writeUInt16LE(s, ofset); satir.writeDoubleLE(t, ofset + 2);
    satir.writeDoubleLE(d / 10, ofset + 10); ofset += 18;
    if (oncekiDeger === null) { govde.push(...varint(d)); oncekiDeger = d; continue; }
    govde.push(...varint(ARALIK - oncekiFark)); oncekiFark = ARALIK;   // damga farkinin farki
    govde.push(...varint(d - oncekiDeger)); oncekiDeger = d;           // deger farki
  }
  const bas = Buffer.alloc(14);                  // taban damga + seri + giris sayisi
  bas.writeDoubleLE(T0 + g * 86_400_000, 0); bas.writeUInt16LE(s, 8); bas.writeUInt32LE(NOKTA, 10);
  hamBayt += bas.length + govde.length;
  bolutBayt += bas.length + deflateSync(Buffer.from(govde)).length;
  bolutSayisi += 1;
}

const N = SERI * GUN * NOKTA;
console.log(`${SERI} seri x ${GUN} gun x ${NOKTA} olcum = ${N} olcum; ` +
  `${bolutSayisi} bolut, bolut basina ${NOKTA} giris`);
console.log(`\n${"yerlesim".padEnd(34)}${"bayt".padStart(11)}${"olcum basina".padStart(14)}${"kat".padStart(7)}`);
for (const [ad, b] of [["satir basina tam damga + deger", satir.length],
    ["bolut, yalniz fark kodlamasi", hamBayt], ["bolut, fark + blok sikistirma", bolutBayt]])
  console.log(ad.padEnd(34) + String(b).padStart(11) + (b / N).toFixed(2).padStart(14) +
    (satir.length / b).toFixed(2).padStart(7));
```

```
12 seri x 30 gun x 1440 olcum = 518400 olcum; 360 bolut, bolut basina 1440 giris

yerlesim                                 bayt  olcum basina    kat
satir basina tam damga + deger        9331200         18.00   1.00
bolut, yalniz fark kodlamasi          1042560          2.01   8.95
bolut, fark + blok sikistirma          329013          0.63  28.36
```

Sayılar **ölçüm** sınıfındadır: iki yerleşim de gerçekten serildi, sıkıştırma gerçekten
çalıştırıldı.

Aynı 518.400 ölçüm satır yerleşiminde **9.331.200 bayt**, bölütlenmiş yerleşimde **329.013
bayttır**: ölçüm başına 18 bayta karşı **0,63 bayt**, yirmi sekiz kat. Ara satır kazancın nereden
geldiğini ayırıyor. Yalnız fark kodlaması, blok sıkıştırma olmadan, ölçüm başına 2,01 bayta
iniyor — dokuz kat. Nedeni verinin biçimidir: ölçüm aralığı sabit olduğu için ardışık damgaların
farkı hep aynıdır, farkın farkı sıfırdır ve tek baytla yazılır; değer farkı da küçük bir
tamsayıdır. Sekiz baytlık damga ve sekiz baytlık değer, iki bayta düşer. Blok sıkıştırma bunun
üstüne üç kat daha ekliyor: tekrarlayan sıfır ve küçük fark dizisi sıkıştırıcının en verimli
çalıştığı girdidir.

Bedel aynı kararın içindedir. Bölüt sıkıştırılmış bir bloktur; içindeki tek bir ölçüme bölütün
tamamı çözülmeden erişilemez — **erişimin birimi ölçüm değil, bölüttür**. Kazanç da verinin
sıralı ve düzenli aralıklı olmasına bağlıdır: ölçümler gecikmeli ve karışık sırada gelirse damga
farkının farkı sıfır olmaz ve bölüt şişer.

## Zaman Aralığı Sorgusu

Bu ailenin sorguları hemen her zaman bir zaman aralığı taşır. Satır yerleşimi zamana göre sıralı
tek dizidir: aralığın başı ikili aramayla bulunur, ama pencere içindeki **bütün kaynakların**
ölçümleri okunur. Bölüt yerleşiminde üstveri (kaynak kimliği ve taban damga) sorguyla kesişmeyen
bölütleri hiç açmadan eler.

```js
// zaman/aralik.mjs — ayni olcum kumesi iki yerlesimde sorgulaniyor. Satir yerlesimi zamana
// gore sirali tek dizidir: aralik ikili aramayla bulunur, pencere icindeki butun seriler
// taranir. Bolut yerlesiminde ustveri (seri, taban damga) eleme yapar, ama acilan bolut
// butunuyle cozulur. Sayilar kosumdan bagimsizdir.
const SERI = 12, GUN = 30, ARALIK = 60_000, T0 = Date.UTC(2024, 5, 1), NOKTA = 1440;
const seri = [], damga = [];                     // satir yerlesimi: zamana gore sirali
for (let g = 0; g < GUN; g += 1) for (let i = 0; i < NOKTA; i += 1)
  for (let s = 0; s < SERI; s += 1) { seri.push(s); damga.push(T0 + g * 86_400_000 + i * ARALIK); }
const bolut = [];                                // bolut yerlesimi: bolut = (seri, gun)
for (let s = 0; s < SERI; s += 1) for (let g = 0; g < GUN; g += 1)
  bolut.push({ seri: s, taban: T0 + g * 86_400_000, son: T0 + (g + 1) * 86_400_000, giris: NOKTA });

function satirSorgu(s, alt, ust) {               // ikili arama + pencere taramasi
  let a = 0, b = damga.length;
  while (a < b) { const o = (a + b) >> 1; if (damga[o] < alt) a = o + 1; else b = o; }
  let taranan = 0, sonuc = 0;
  for (let i = a; i < damga.length && damga[i] < ust; i += 1) {
    taranan += 1; if (s === null || seri[i] === s) sonuc += 1; }
  return { birim: 0, taranan, sonuc };
}
function bolutSorgu(s, alt, ust) {               // ustveriyle eleme, sonra bolutu ac
  let acilan = 0, taranan = 0, sonuc = 0;
  for (const b of bolut) {
    if (s !== null && b.seri !== s) continue;
    if (b.son <= alt || b.taban >= ust) continue;
    acilan += 1; taranan += b.giris;             // bolut butunuyle cozulur
    for (let i = 0; i < b.giris; i += 1) { const t = b.taban + i * ARALIK;
      if (t >= alt && t < ust) sonuc += 1; }
  }
  return { birim: acilan, taranan, sonuc };
}

const G12 = T0 + 11 * 86_400_000;
const sorgular = [["3 nolu raf, 12 Haziran (tam gun)", 3, G12, G12 + 86_400_000],
  ["butun raflar, 12-14 Haziran", null, G12, G12 + 3 * 86_400_000],
  ["3 nolu raf, 12 Haziran 09:00-10:00", 3, G12 + 9 * 3_600_000, G12 + 10 * 3_600_000],
  ["3 nolu raf, butun ay", 3, T0, T0 + GUN * 86_400_000]];
console.log(`satir yerlesimi ${damga.length} giris; bolut yerlesimi ${bolut.length} bolut`);
console.log(`\n${"sorgu".padEnd(35)}${"satir: taranan".padStart(15)}${"bolut".padStart(7)}` +
  `${"bolut: taranan".padStart(15)}${"sonuc".padStart(8)}`);
for (const [ad, s, alt, ust] of sorgular) {
  const x = satirSorgu(s, alt, ust), y = bolutSorgu(s, alt, ust);
  if (x.sonuc !== y.sonuc) throw new Error("iki yol ayni sonucu vermedi");
  console.log(ad.padEnd(35) + String(x.taranan).padStart(15) + String(y.birim).padStart(7) +
    String(y.taranan).padStart(15) + String(x.sonuc).padStart(8));
}
```

```
satir yerlesimi 518400 giris; bolut yerlesimi 360 bolut

sorgu                               satir: taranan  bolut bolut: taranan   sonuc
3 nolu raf, 12 Haziran (tam gun)             17280      1           1440    1440
butun raflar, 12-14 Haziran                  51840     36          51840   51840
3 nolu raf, 12 Haziran 09:00-10:00             720      1           1440      60
3 nolu raf, butun ay                        518400     30          43200   43200
```

Dört satırın üçü bölüt yerleşiminin lehinedir; modelin sınırı dördüncüsündedir.

Tek bir rafın bir günü satır yerleşiminde **17.280 giriş** taratıyor, bölüt yerleşiminde **tek
bölüt** açılıyor ve 1.440 giriş okunuyor. Oran tam olarak kaynak sayısıdır: sıralı dizide bir
rafın ölçümleri on iki rafınkiyle iç içe durur ve aralık daraltılsa da o iç içelik kalır. Aynı
rafın bütün ayında katsayı korunuyor: 518.400 giriş taramaya karşı 30 bölüt ve 43.200 giriş.

İkinci satır bölütlemenin **hiçbir şey kazandırmadığı** durumu gösteriyor: sorgu bütün rafları
istediğinde eleyecek üstveri kalmaz, iki yol da 51.840 giriş okur ve bölüt yoluna yalnız 36
bölüt açma işi eklenir.

Üçüncü satır bedeli taşıyor. Bir saatlik pencere satır yerleşiminde **720 giriş** taratıyor;
bölüt yerleşiminde aynı soru **1.440 giriş** çözdürüyor, çünkü açılan bölüt bir günlüktür ve
sıkıştırılmış blok kısmen çözülemez. Sonuç iki yolda da 60 ölçümdür.
**Bölütün süresi, aralık sorgusunun en küçük okuma birimidir**: bölüt
büyüdükçe sıkıştırma kazancı artar, dar aralık sorgusunun israfı da artar. Dar aralıklar sık
soruluyorsa karşılığı ölçümü daha kaba çözünürlükte ikinci kez saklamaktır — bu kararın adı
**seyreltmedir** ve Vaka Çalışmaları kursunda ölçülmüştür.

## Saklama Politikası

Zaman serisi verisinin ötekilerden ayrıldığı son nokta silinmesidir. Bir kitap kaydı silinmez;
bir dakikalık ölçüm belirli bir yaştan sonra hiçbir soruya girmez ve **saklama (retention)**
politikası onu düşürür. Soru bu düşürmenin neye mal olduğudur. **NS27:** saklama süresi yedi
gündür.

```js
// zaman/saklama.mjs — saklama politikasinin iki yerlesimde uygulanmasi. Satir yerlesiminde
// suresi dolan her kayit tek tek dusurulur. Bolut yerlesiminde tamami suresi dolmus bolut
// birim olarak dusurulur; yalniz siniri kesen bolut yeniden yazilir. Sayilar kosumdan
// bagimsizdir.
const SERI = 12, GUN = 30, ARALIK = 60_000, T0 = Date.UTC(2024, 5, 1), NOKTA = 1440;
const SIMDI = T0 + GUN * 86_400_000;
const damga = [];                                // satir yerlesimi: zamana gore sirali
for (let g = 0; g < GUN; g += 1) for (let i = 0; i < NOKTA; i += 1)
  for (let s = 0; s < SERI; s += 1) damga.push(T0 + g * 86_400_000 + i * ARALIK);
const bolut = [];
for (let s = 0; s < SERI; s += 1) for (let g = 0; g < GUN; g += 1)
  bolut.push({ taban: T0 + g * 86_400_000, son: T0 + (g + 1) * 86_400_000, giris: NOKTA });

function satirSaklama(sinir) {                   // her kayit tek tek dusurulur
  let dokunulan = 0, dusen = 0;
  for (const t of damga) { dokunulan += 1; if (t < sinir) dusen += 1; else break; }
  return { birim: "kayit", dokunulan, dusen, kalan: damga.length - dusen };
}
function bolutSaklama(sinir) {                   // ustveri okunur, bolut birim olarak duser
  let dokunulan = 0, dusen = 0, yeniden = 0;
  for (const b of bolut) {
    if (b.son <= sinir) { dusen += b.giris; continue; }          // hicbir giris acilmaz
    if (b.taban >= sinir) continue;
    dokunulan += b.giris;                                        // siniri kesen bolut
    for (let i = 0; i < b.giris; i += 1)
      if (b.taban + i * ARALIK < sinir) dusen += 1; else yeniden += 1;
  }
  return { birim: "bolut", dokunulan, dusen, kalan: SERI * GUN * NOKTA - dusen, yeniden };
}

console.log(`${damga.length} olcum, ${bolut.length} bolut, bolut basina ${NOKTA} giris`);
console.log(`\n${"saklama".padEnd(22)}${"yerlesim".padEnd(9)}${"dokunulan giris".padStart(16)}` +
  `${"dusen kayit".padStart(13)}${"kalan".padStart(8)}`);
for (const [ad, sure] of [["7 gun (bolut siniri)", 7 * 86_400_000],
    ["7 gun 12 saat", 7.5 * 86_400_000]]) {
  const sinir = SIMDI - sure;
  for (const r of [satirSaklama(sinir), bolutSaklama(sinir)])
    console.log(ad.padEnd(22) + r.birim.padEnd(9) + String(r.dokunulan).padStart(16) +
      String(r.dusen).padStart(13) + String(r.kalan).padStart(8));
}
const y = bolutSaklama(SIMDI - 7.5 * 86_400_000);
console.log(`\nsiniri kesen bolutler: ${y.dokunulan / NOKTA} bolut acildi, ` +
  `${y.yeniden} giris yeniden yazildi`);
```

```
518400 olcum, 360 bolut, bolut basina 1440 giris

saklama               yerlesim  dokunulan giris  dusen kayit   kalan
7 gun (bolut siniri)  kayit              397441       397440  120960
7 gun (bolut siniri)  bolut                   0       397440  120960
7 gun 12 saat         kayit              388801       388800  129600
7 gun 12 saat         bolut               17280       388800  129600

siniri kesen bolutler: 12 bolut acildi, 8640 giris yeniden yazildi
```

Birinci çift bölütlemenin en keskin kazancıdır. İki yerleşim de aynı **397.440 kaydı** düşürüp
arkasında aynı 120.960 kaydı bırakıyor, ama satır yerleşimi bunun için 397.441 girişe dokunuyor:
düşen her kayda bir kez, artı sınırı gösteren bir kayda daha. Bölüt yerleşiminde dokunulan giriş
**sıfırdır** — yalnız 360 bölüt üstverisi okunur ve süresi dolmuş 276 bölüt (397.440 ÷ 1.440,
**hesap**) dosya olarak düşürülür. Silme burada veri işlemi değil, dosya işlemidir.

Sıfır, saklama sınırının bölüt sınırına denk gelmesine bağlıdır. İkinci çift bunu bozuyor: yedi
buçuk günlük saklamada sınır bölütlerin ortasından geçer, on iki bölüt açılır, **17.280 girişe**
dokunulur ve bunun 8.640'ı bölütler yeniden yazıldığı için ikinci kez saklanır. Düşen kayıt
sayısı gene iki yolda aynıdır; değişen yalnız iş miktarıdır. Kural şudur: **saklama süresi bölüt
süresinin katı seçildiğinde silme bedeli sıfıra iner**; seçilmediğinde bedel, sınırı kesen bölüt
sayısı çarpı bölüt boyudur.

Bölüt süresi kararının üçüncü yüzü budur: bölüt büyüdükçe saklanan bayt düşer, dar aralık
sorgusunun israfı ve saklama sınırındaki yeniden yazma artar. Üçünü birden en iyileyen bir değer
yoktur.

## Özet

- Zaman serisi verisi sona eklemeli yazılır, aralıkla okunur ve yaşlandığında düşürülür; depo bu
  üç özelliği veri modelinin içine alır.
- Satır başına tam damga ve değer 18 bayt tutarken, taban damga + fark kodlaması 2,01 bayta, blok
  sıkıştırma eklendiğinde 0,63 bayta iniyor — 9.331.200 bayta karşı 329.013 bayt, 28,4 kat.
  Kazanç verinin sıralı ve düzenli aralıklı olmasına bağlıdır; bedeli, erişim biriminin ölçüm
  değil bölüt olmasıdır.
- Bölüt üstverisi aralık sorgusunda eleme yapar: bir rafın bir günü 17.280 giriş taraması yerine
  tek bölüt ve 1.440 giriştir. Sorgu bütün kaynakları istediğinde eleme yoktur, iki yol da 51.840
  giriş okur.
- Bölüt süresi en küçük okuma birimidir: bir saatlik pencere satır yerleşiminde 720 giriş, bölüt
  yerleşiminde 1.440 giriş çözdürür ve sonuç 60 ölçümdür.
- Saklama sınırı bölüt sınırına denk geldiğinde 397.440 kayıt hiçbir girişe dokunulmadan, 276
  bölüt dosya olarak düşürülerek silinir; denk gelmediğinde 12 bölüt açılır, 17.280 girişe
  dokunulur, 8.640 giriş yeniden yazılır.

## Sonraki Adım

Beş aile de aynı sırayla anlatıldı: modelin ayırt edici mekaniği, ucuzlattığı ve pahalılaştırdığı
sorgu sınıfları. Ortaya çıkan tablo bir sıralama değildir — her aile bir erişim örüntüsünü
ucuzlatmak için başkasını pahalılaştırdı ve her ölçümde ters yönlü satır da göründü.

Geriye karar kalıyor: eldeki iş için hangi aile. Sonraki ders bu soruyu tercihle değil ölçütle
karşılıyor — erişim örüntüsünün hangi özelliği hangi aileye işaret eder, aynı iş yükü iki ailede
koşturulduğunda fark hangi eşikte anlam kazanır, ve bir aile yanlış seçildiğinde bedel nerede
görünür.
