---
title: 'Veri Depolama Mimarileri'
source: 'https://academia.sh/tr/kurslar/veri-depolama-mimarileri'
language: tr
updated: '2026-08-17T04:40:12+00:00'
license: 'CC BY-SA 4.0'
lessons: 12
outcomes:
    - 'Veri ambarı, gölü ve göl evi yaklaşımlarını aynı veri üzerinde okuma maliyeti ve yakalama gecikmesiyle karşılaştırır'
    - 'Boyutsal modelleme kararlarının birleştirme sayısına ödettiğini ölçer ve tarihçe tutmayan boyutun cevabı nasıl yanlışladığını gösterir'
    - 'Bölümleme, dosya düzeni ve sütunlu okuma kararlarıyla sorgu maliyetini düşürür ve küçük dosya sorununun nerede başladığını sayar'
---

# Veri Depolama Mimarileri

Aynı satırların nerede ve hangi düzende durduğunun bir sorguya ne ödettiğinin sayılması: şema dayatmanın reddettiği satır ile karşılığında okunan hücre, ham düzende yazmanın ucuz okumanın pahalı olması ve hangi bozulmanın yazma anında yakalanamadığı, şema dayatmasının okuma anına taşınmasıyla değişen yakalama gecikmesi, alan odaklı alt kümenin çoğalttığı satır ve tutarsızlık riski, sahiplik dağıtıldığında düzen kararı alan taraf ile bir sorgunun dokunduğu düzen sayısı, katalog varken ve yokken bir varlığı bulmak için bakılan giriş ve katalogun bayatlaması, olgu ile boyutun ayrılmasının sakladığı hücre ve birleştirme, yıldızda 2200 birleştirme ve 212 boyut satırına karşı kar tanesinde 4400 ve 217 ile birebir aynı sonuç, üzerine yazan boyutun sorguyu yavaşlatmadan cevabı yanlışlaması ve 2748 metreküpü yanlış kovaya göndermesi, iki sütun seçildiğinde sütunlu 400 satırlı 1400 hücre ve farkın kapandığı nokta, bölüm anahtarı dışındaki süzgeçte maliyetin 4520'den 11000'e çıkması ile budayan sorguda bile ince bölümlemenin 520'den 1600'e çıkarması, arşive taşımanın kazandırdığı ve geriye dönük yanıtlanamaz kıldığı soru.

## Depolama Yaklaşımları

Aynı verinin dört ayrı yaklaşımla saklanmasının yazma ve okuma tarafına ne ödettiğinin sayılması: şemaya uyumlu depolamada dayatmanın reddettiği satır ile sorgunun okuduğu hücre, ham depolamada yazmanın ucuz okumanın pahalı olması ve hangi bozulma sınıfının yazma anında hiç yakalanamadığı, ham katman korunurken şema dayatmasının okuma anına taşınmasıyla yakalama gecikmesinin aynı birimle karşılaştırılması, bir bölgeye ait alt küme ayrı yazıldığında sorgu maliyeti ile aynı satırın kaç yerde durduğu, sahiplik dağıtıldığında düzen kararı alan taraf sayısı ile uçtan uca bir sorgunun dokunduğu düzen sayısı ve dosya sabitinin 60'tan 480'e süpürülmesiyle hangi sonuçların sabite duyarlı olduğu, katalog varken ve yokken bir varlığı bulmak için bakılan giriş sayısı ile katalogun kaç girişinin artık var olmayan bir düzeni gösterdiği.

### Veri Ambarı

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/veri-ambari

Hattan çıkan 2200 satırın bir depolama düzenine indirilip bir sorgunun o düzene dokunmak için ödediğinin sayılması: bölümlemesiz ve satırlı tabanda dönem süzgeçli sorgu 15400 hücre ve 15520 maliyet, yalnız seçilen sütunlar okunduğunda 4400 hücre ve 4520 maliyet, dönem bölümlemesinde 400 hücre ve 520 maliyet. Aynı düzen bölüm anahtarı dışındaki süzgeçte 4520 yerine 5720 veriyor, yani düzen bir sorguya kazandırırken ötekine kaybettiriyor. Yazma anında şema dayatması altı bozulma sınıfının ikisini yakalayıp 400 satırı reddediyor, ama reddedilen 400 satırın 400'ünde ölçüm bozulmamış duruyor; şema satırı atıyor, sayıyı değil. Dosya sabiti 120 bir seçimdir: dönem sorgusundaki 4000 hücrelik kazanç sabitten bağımsız, öteki sorgudaki 1200'lük kayıp ise tamamen sabitin eseri ve on dosyaya eşit.

Veri Mühendisliğine Giriş kursu on derste bir hattı kurdu ve saydı. Kurgu ölçüm ağının 2400 sayaç
okuması beş aşamadan geçti, tüketim aşamasının çıkışında 2200 satır kaldı, özet 55 hücreye ve sunum
5 değere indi; ardından altı bozulma sınıfının her birinin kaç aşama sonra yakalandığı ölçüldü. Bu
ölçülerin tamamı hattın **taşıdığı** satır üzerineydi. Hiçbiri şunu sormadı: o 2200 satır **nerede
duruyor**, hangi düzende yazılıyor ve o düzen bir sorguya ne ödetiyor.

Soru boş değildir: aynı 2200 satır birbirinden çok farklı biçimlerde diske indirilebilir ve seçilen
biçim bir sorgunun bedelini kat kat değiştirir. Bu ders o bedeli ölçülebilir kılar. Bir **depolama
düzeni (storage layout)** kurar, bir sorgunun o düzene dokunmak için ödediğini sayar ve ilk düzen
kararını, yani **şemanın yazma anında dayatılmasını**, hem kattığı hem ödettiği yandan yazar. Kursun
kuralı buradan çıkar ve on iki ders sürer: **bir depolama düzeninin sayısı sakladığı satır değil,
bir sorgunun ona dokunmak için ödediğidir; hangi sorgu için seçildiği yazılmayan düzen ölçülmemiş
sayılır.**

- **DY1.** Kod bir **kurgudur**; gerçek bir ambar ürünü, dosya biçimi, katalog ya da sorgu motoru
  çağrılmaz ve hiçbir ürün, satıcı ya da biçim adı yazılmaz. Yaklaşımlar **sınıf adlarıyla** anılır;
  SQL bir dildir ve serbesttir. Kaynak ile hat Veri Mühendisliğine Giriş kursundan devralınır, tohum
  **20260218**'dir.
- **DY2.** Kaynak **200 abone x 12 dönem = 2400 okuma**; hattan çıkan satır **2200**, çünkü her
  abonenin ilk döneminin tüketim farkı tanım gereği yoktur.
- **DY3.** Bir **düzen**, satırların hangi dosyaya düştüğüdür. `yaz` satırları **bölümleme
  anahtarına** göre dağıtır; anahtar boşsa tek dosya oluşur.
- **DY4.** Bir sorgunun bedeli üç sayıdır: **dokunulan dosya**, **okunan hücre** ve **maliyet =
  dosya x 120 + hücre**. Süzgeç bölüm anahtarındaysa dosya budanır, değilse bütün dosyalara
  dokunulur.
- **DY5. Dosya sabiti 120 bir seçimdir**; ona duyarlı her sonuç sabitin değeriyle yazılır.
- **DY6.** Taban çizgisi **bölümlemesiz, satırlı, tarihçesiz** düzendir; bu üç eksenin yalnız
  birincisi bu derste oynatılır.
- **DY7.** Altı bozulma sınıfı Veri Mühendisliğine Giriş kursundan **girdi** alınır, yeniden
  türetilmez: `gec_gelen`, `yinelenen`, `sema_degisti`, `birim_degisti`, `eksik_bolum`, `kayan_tip`.
  Hiçbiri saldırı değildir; hepsi üretici tarafı kazasıdır.
- **DY8.** Bölümleme, dizin ve sorgu planı yordamı **tekrarlanmaz**; Veri Modelleme ve İlişkisel
  Kuram ile Sorgu Eniyileme kurslarında ölçüldü. Eklenen tek şey **dosya düzeyindeki maliyettir**.
- **DY9.** Çözünürlük: 2200 satırda satır oranının adımı **0,0005**, en çok 110 dosyada dosya
  oranının adımı **0,0091**. **Maliyet oran değildir**; değerin kendisi yazılır.

#### Düzen, Dosya ve Maliyet

**Veri ambarı (data warehouse)**, şemaya uyumlu ve yapılandırılmış veriyi saklayan depolama
sınıfıdır. Onu iki karar tanımlar. Birincisi **şemanın yazma anında dayatılmasıdır**: bir satır
ambara girebilmek için alanlarını ve türlerini önceden bildirilmiş şemaya uydurmak zorundadır.
İkincisi **düzenin önceden seçilmiş olmasıdır**: satırların hangi dosyaya düşeceğine yazma sırasında
karar verilir, okuma sırasında değil. İkinci karar ölçülebilir, çünkü bir sorgunun bedeli doğrudan
ondan çıkar. Aşağıdaki kod hattı kurar, çıkışını bir düzene indirir ve iki sorguyu üç düzende ölçer.
İkisi de aynı iki sütunu seçer; sorgu A'nın süzgeci `donem`, sorgu B'ninki `tip` alanındadır.

```python
# KURGU depolama duzeni -- gercek bir ambar urunu, dosya bicimi ya da sorgu
# motoru cagrilmaz. Kaynak bir olcum aginin KURGU sayac okumalaridir, tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


# ---- DEPOLAMA DUZENI: yaz , sorgu , maliyet
SUTUN = ("abone", "donem", "bolge", "tip", "endeks", "birim", "m3")
DOSYA_SABIT = 120          # her dosyaya dokunmanin sabit bedeli , hucre cinsinden


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def yaz(kayitlar, bolumleme=()):
    """Kayitlari bolumleme anahtarina gore dosyalara dagitir."""
    duzen = {}
    for k in kayitlar:
        anahtar = tuple(k[a] for a in bolumleme)
        duzen.setdefault(anahtar, []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None, sutunlu=True):
    """Suzgec: {alan: deger}. Bolumleme anahtarindaki alanlar dosya budar ,
    obur alanlar dosyanin tamamini okutur."""
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    kalan = {a: v for a, v in suzgec.items() if a not in bolumleme}
    dosya, hucre, satir = 0, 0, 0
    okunan_sutun = len(secilen) if sutunlu else len(SUTUN)
    for anahtar, kayit in duzen.items():
        if budayan:
            eslesme = dict(zip(bolumleme, anahtar))
            if any(eslesme[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * okunan_sutun
        satir += sum(1 for k in kayit if all(k[a] == v for a, v in kalan.items()))
    return {"dosya": dosya, "hucre": hucre, "eslesen_satir": satir,
            "maliyet": dosya * DOSYA_SABIT + hucre}


kayitlar = satirlar()
taban = yaz(kayitlar, ())
duzenli = yaz(kayitlar, ("donem",))
SEC, OLCUM = ("bolge", "m3"), {}
print("hattan cikan satir", len(kayitlar), " secilen sutun", len(SEC), "/", len(SUTUN))
print("duzen                  dosya  ort.satir  A.dosya  A.hucre  A.maliyet  B.maliyet")
for ad, d, b, st in [("bolumlemesiz/satirli", taban, (), False),
                     ("bolumlemesiz", taban, (), True),
                     ("donem", duzenli, ("donem",), True)]:
    a = sorgu(d, b, SEC, {"donem": "2026-07"}, sutunlu=st)
    v = sorgu(d, b, SEC, {"tip": "isyeri"}, sutunlu=st)
    OLCUM[ad] = (a, v)
    print(f"{ad:<22}{len(d):>6}{len(kayitlar) / len(d):>11.1f}"
          f"{a['dosya']:>9}{a['hucre']:>9}{a['maliyet']:>11}{v['maliyet']:>11}")
```

```
hattan cikan satir 2200  secilen sutun 2 / 7
duzen                  dosya  ort.satir  A.dosya  A.hucre  A.maliyet  B.maliyet
bolumlemesiz/satirli       1     2200.0        1    15400      15520      15520
bolumlemesiz               1     2200.0        1     4400       4520       4520
donem                     11      200.0        1      400        520       5720
```

Tablo kursun **üçlüsünü** kuruyor ve bundan sonraki her derste aynı üç sayı yan yana duracak.
**Düzensiz taban** ilk satırdır: tek dosya, satırlı okuma, sorgu A için 15400 hücre ve **15520
maliyet**. **Düzenli kurulum** son satırdır: `donem` anahtarına göre on bir dosya, sorgu A için 400
hücre ve **520 maliyet**. **Bedel** bu derste iki kalemdir: sorgu B'nin maliyeti ile birazdan
ölçülecek reddedilen satır.

Orta satır iki ekseni ayırır. Yalnız seçilen iki sütunun okunması hücreyi 15400'den 4400'e
indiriyor; bu bir **okuma biçimi** kazancıdır ve kursun ilerleyen bir dersinde ayrıca ölçülecek.
Bölümleme ayrı bir eksendir: 4400'den 400'e inişin tamamı **bölüm ayıklamasıdan (partition pruning)**
gelir. Süzgeç `donem` alanında ve düzen de `donem` anahtarıyla kurulu olduğu için sorgu on bir
dosyanın onunu hiç açmıyor.

Son sütun kursun **ikinci iddiasının** ilk görünümüdür: **düzen de bozar.** Aynı `donem` düzeni,
süzgeci `tip` alanında olan sorgu B için **5720** veriyor; bölümlemesiz taban aynı sorguyu **4520**
ile bitiriyor. Bölümleme burada hiçbir dosya budayamıyor, çünkü `tip` anahtarda değil; okunan hücre
iki düzende de 4400, ama on bir dosyanın her birine ayrı dokunuluyor. Bir düzen bir sorguya
kazandırırken bir başkasına kaybettirir ve iki sonuç aynı tabloda durur. Kursun kuralı tam olarak
bunu söyler: **hangi sorgu için seçildiği yazılmayan düzen ölçülmemiş sayılır.**

#### Şema Dayatmanın Reddettiği Satır

Şema yazma anında dayatıldığında ambar bir **kapı** kazanır: uymayan satır depoya hiç girmez. Bu
kapının ne kattığı ve ne ödettiği, altı bozulma sınıfı kapıya sürüldüğünde görünür.

```python
# ---- KURGU bozulma siniflari. Hicbiri saldiri degildir; hepsi uretici tarafi kazasidir.
BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                # 60 kayit iki kez geldi
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI , sessiz bozulma
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":               # bir bolgenin bolumu hic yazilmadi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])          # sayi metne dondu
            sayac += 1
            yeni.append(y)
    return yeni, sayac


# ---- SEMA YAZMA ANINDA DAYATILIR: uymayan satir hic yazilmaz
SEMA = {"abone": str, "donem": str, "bolge": str, "tip": str, "endeks": int, "birim": str}


def sema_ihlali(k):
    for a, t in SEMA.items():
        if a not in k:
            return "eksik_alan"
        if not isinstance(k[a], t):
            return "yanlis_tip"
    return None


def yaz_semali(kayitlar):
    kabul, red = [], {}
    for k in kayitlar:
        n = sema_ihlali(k)
        if n:
            red[n] = red.get(n, 0) + 1
            continue
        kabul.append(k)
    return kabul, red


def olcum_kurtarilabilir(k):
    """Reddedilen satirin olcumu duruyor mu: adi degismis ya da metne donmus olabilir."""
    d = k.get("endeks", k.get("endeks_degeri"))
    try:
        int(d)
        return True
    except (TypeError, ValueError):
        return False


kaynak = kaynak_uret()
print(f"{'sinif':<15}{'gelen':>7}{'bozulan':>9}{'reddedilen':>12}  {'neden':<12}{'yazilan':>8}")
top_red, top_kurt, yakalanan = 0, 0, 0
for s in ["temiz"] + BOZULMA:
    gelen, n = (kaynak, 0) if s == "temiz" else boz(kaynak, s)
    kabul, red = yaz_semali(gelen)
    r = sum(red.values())
    neden = "-" if not red else "/".join(sorted(red))
    top_red += r
    top_kurt += sum(1 for k in gelen if sema_ihlali(k) and olcum_kurtarilabilir(k))
    yakalanan += 1 if r else 0
    print(f"{s:<15}{len(gelen):>7}{n:>9}{r:>12}  {neden:<12}{len(kabul):>8}")
print(f"reddedilen toplam {top_red}  olcumu duran {top_kurt}  "
      f"yakalanan sinif {yakalanan}/{len(BOZULMA)}")
```

```
sinif            gelen  bozulan  reddedilen  neden        yazilan
temiz             2400        0           0  -               2400
gec_gelen         2360       40           0  -               2360
yinelenen         2460       60           0  -               2460
sema_degisti      2400      200         200  eksik_alan      2200
birim_degisti     2400      200           0  -               2400
eksik_bolum       2354       46           0  -               2354
kayan_tip         2400      200         200  yanlis_tip      2200
reddedilen toplam 400  olcumu duran 400  yakalanan sinif 2/6
```

Kapının **kattığı** ilk satırda okunur: temiz kaynakta hiçbir satır reddedilmiyor, yani şema
dayatması sağlam veriyi engellemiyor. Bozuk kaynaklarda iki sınıf kapıya takılıyor. `sema_degisti`
alanın adını değiştirdiği için 200 satır **eksik alan**, `kayan_tip` sayıyı metne çevirdiği için 200
satır **yanlış tür** gerekçesiyle geri çevriliyor. Depoya giren her satırın alanları ve türleri
bilindiği için önceki bölümdeki sorgu istediği iki sütunu adıyla seçebiliyor ve 400 hücreyle
bitiriyor: **şema dayatmasının kattığı, sorgunun okuduğu hücreyi küçültme hakkıdır.**

**Ödettiği** iki kalemdir. Birincisi kapsam: altı sınıfın **dördü** kapıdan etkilenmeden geçiyor.
`gec_gelen` ile `eksik_bolum` satır **eksiltiyor**, ortada uymayan bir satır yok; şema gelmeyeni
göremez. `yinelenen` şemaya uygun satırı ikinci kez yazıyor, çünkü şema alan ve tür sorar, tekillik
sormaz. `birim_degisti` en tehlikelisidir: değeri bin katına çıkarıp birim etiketini `m3` bırakıyor,
yani **şemaya kusursuz uyuyor**.

İkinci kalem kursun ikinci iddiasının buradaki karşılığıdır: **düzen de bozar.** Reddedilen 400
satırın **400'ünde ölçüm bozulmadan duruyor** — `sema_degisti` satırında `endeks_degeri` adıyla,
`kayan_tip` satırında metin olarak, ama ikisinde de tamsayıya çevrilebilir hâlde. Şema kapısı
**satırı** atıyor, **sayıyı** değil. Bir düzen kararı burada veri kalitesini yükseltmiyor,
kurtarılabilir veriyi **yok ediyor** ve bunu sessizce yapıyor, çünkü reddedilen satır sayısı ayrıca
yazılmadıkça hiçbir sorguda görünmez.

#### Dosya Sabiti Bir Seçimdir

Maliyet formülündeki 120 sayısı ölçülmüş bir büyüklük değil, **modelin bir seçimidir**: her dosyaya
dokunmanın hücre cinsinden bedelini temsil eder. Seçim olduğu için ondan çıkan her sonucun sabite ne
kadar duyarlı olduğu ayrıca yazılmalıdır.

```python
def maliyet(r, sabit):
    return r["dosya"] * sabit + r["hucre"]


(A_t, B_t), (A_d, B_d) = OLCUM["bolumlemesiz"], OLCUM["donem"]
print(" sabit  A.taban  A.duzenli  A.kazanc  B.taban  B.duzenli  B.kazanc")
for s in (0, 120):
    print(f"{s:>6}{maliyet(A_t, s):>9}{maliyet(A_d, s):>11}{maliyet(A_t, s) - maliyet(A_d, s):>10}"
          f"{maliyet(B_t, s):>9}{maliyet(B_d, s):>11}{maliyet(B_t, s) - maliyet(B_d, s):>10}")
print("dokunulan dosya farki  A", A_d["dosya"] - A_t["dosya"], " B", B_d["dosya"] - B_t["dosya"])
```

```
 sabit  A.taban  A.duzenli  A.kazanc  B.taban  B.duzenli  B.kazanc
     0     4400        400      4000     4400       4400         0
   120     4520        520      4000     4520       5720     -1200
dokunulan dosya farki  A 0  B 10
```

İki satır sonucu kesin biçimde ayırıyor. Bölümlemenin sorgu A'ya **kazandırdığı 4000 hücre sabitten
bağımsızdır**: sabit sıfır da olsa yüz yirmi de olsa kazanç aynı kalır, çünkü iki düzen de tek
dosyaya dokunuyor ve fark tamamen okunan hücreden geliyor. Sorgu B'deki **1200 birimlik kayıp ise
tamamen sabitin eseridir**: sabit sıfırken kayıp yoktur, yüz yirmiyken kayıp on dosya çarpı yüz
yirmidir. Bu dersin "düzen de bozar" bulgusu sabitin **pozitif** olmasına bağlıdır ve büyüklüğü
sabitle doğru orantılıdır. Sabitin birden çok değerle süpürülmesi ve hangi sonuçların sıra
değiştirdiği kursun ilerleyen bir dersinde ölçülür.

Tablodaki hiçbir sayı koşuma bağlı değildir. Hat belirlenimcidir, `random` kullanılmaz ve her hücre
koşum yapılmadan türetilebilir: hattan çıkan satır 200 abone x 11 dönem = **2200**; `donem`
düzenindeki dosya sayısı tüketim hesaplanabilen dönem kadar, **11**; dosya başına ortalama 2200 / 11
= **200** satır; iki sütun seçili bölümlemesiz okuma 2200 x 2 = **4400** hücre; budayan sorguda
200 x 2 = **400** hücre. Türetilebilirlik ölçünün yeterliliği için gereklidir: sonraki derslerde iki
düzen arasında görülen her fark düzen kararından gelir, koşum oynaklığından değil.

#### Özet

- Bir depolama düzeni satırların hangi dosyaya düştüğüdür; bedeli üç sayıdır: dokunulan dosya,
  okunan hücre ve maliyet = dosya x 120 + hücre. Dosya sabiti 120 bir seçimdir.
- Aynı sorgu bölümlemesiz ve satırlı tabanda 15400 hücre ile 15520 maliyet, yalnız seçilen sütunlar
  okunduğunda 4400 hücre ile 4520 maliyet, `donem` bölümlemesinde 400 hücre ile 520 maliyet veriyor.
- Aynı `donem` düzeni, süzgeci anahtarın dışında olan sorgu B için 4520 yerine 5720 veriyor: düzen
  bir sorguya kazandırırken bir başkasına kaybettirir ve bu kayıp tamamen dosya sabitinden gelir.
- Yazma anında şema dayatması altı bozulma sınıfının ikisini yakalayıp 400 satırı reddediyor;
  `gec_gelen`, `yinelenen`, `eksik_bolum` ve `birim_degisti` kapıdan etkilenmeden geçiyor.
- Reddedilen 400 satırın 400'ünde ölçüm bozulmadan duruyor: şema kapısı satırı atıyor, sayıyı değil.
- Kursun kuralı: bir düzenin sayısı sakladığı satır değil bir sorgunun ona dokunmak için ödediğidir;
  hangi sorgu için seçildiği yazılmayan düzen ölçülmemiş sayılır.

#### Sonraki Adım

Bu dersin ambarı kapıyı sıkı tuttu ve karşılığında sorguya ucuz bir okuma verdi. Kapının bedeli de
ölçüldü: dört bozulma sınıfı zaten geçiyor, geri çevrilen 400 satırın ölçümü ise kurtarılabilir
olduğu hâlde depoya hiç girmiyor. Buradan doğal bir soru çıkar: kapı hiç kurulmasaydı ne olurdu.
Gelen her satır olduğu gibi, hiçbir şema dayatılmadan yazılsaydı yazma tarafı ne kazanır, okuma
tarafı ne öderdi. Sonraki ders bu yaklaşımı, yani **veri gölünü** ölçer; ölçüsü iki sayının
karşıtlığıdır: aynı sorgu ham düzende kaç hücre okutuyor, şemaya uyumlu düzende kaç. Tablonun ikinci
yarısı daha rahatsız edici olacak, çünkü kapı kaldırıldığında hangi bozulma sınıfının yazma anında
**hiç** yakalanamadığı da sayılacak.

### Veri Gölü

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/veri-golu

Ham veriyi olduğu gibi saklamanın yazma tarafına kazandırdığının ve okuma tarafına ödettiğinin sayılması: göl 2400 satırı 14400 hücreye hiçbir dönüşüm yapmadan yazıyor, ambar 2200 satırı 15400 hücreye yazmak için 7200 satır işliyor; aynı dönem sorgusu gölde 14400 hücre ve 14520 maliyet, ambarda 400 hücre ve 520 maliyet veriyor ve gölün yazmada kazandığı 1000 hücre tek bir sorguda 14000 hücre olarak geri ödeniyor. Altı bozulma sınıfının yalnız ikisi tek satıra bakılarak görülebiliyor; ikisi bütün kümeyi, ikisi özeti gerektiriyor, yani yazma anındaki hiçbir satır kapısı dördünü göremez ve birim değişimi satır ile küme kapsamının ikisinde de görünmez. Aynı göl katmanını okuyan iki tüketici 1800 ve 2200 satır, 45 ve 55 özet hücresi, 48004 ve 58454 metreküp buluyor.

Önceki ders veri ambarını iki kararla tanımladı: şema yazma anında dayatılır ve düzen önceden
seçilir. Kapının ne kattığı ölçüldü, sorgu 400 hücreyle bitti. Bedeli de ölçüldü: altı bozulma
sınıfının dördü kapıdan etkilenmeden geçiyor ve geri çevrilen 400 satırın ölçümü kurtarılabilir
olduğu hâlde depoya hiç girmiyor.

Bu ders karşıt kararı ölçer. **Veri gölü (data lake)**, gelen veriyi **olduğu gibi**, hiçbir şema
dayatmadan ve hiçbir dönüşüm yapmadan saklayan depolama sınıfıdır. Şema yoktur demek yanlış olur;
şema **okuma anına ertelenmiştir** ve okuyan taraf onu kendi taşır. Bu ertelemenin iki yüzü vardır
ve ikisi de sayılabilir: **yazma ucuzlar, okuma pahalılaşır.** Dersin ölçüsü bu iki sayının
karşıtlığıdır; ikinci yarısı ise daha rahatsız edicidir, çünkü yazma anında **hangi bozulma
sınıfının hiç görülemediğini** sayar.

- **DY10.** Kod bir **kurgudur**; hiçbir göl ürünü, dosya biçimi, katalog ya da sorgu motoru adı
  yazılmaz. Kaynak ile hat Veri Mühendisliğine Giriş kursundan devralınır, tohum **20260218**.
- **DY11.** Ham katmanda **alan kümesi bilinmez**. Bu yüzden okuma sırasında sütun seçilemez ve
  bölüm budanamaz: her satırın **bütün alanları** okunur ve tek dosyaya dokunulur.
- **DY12.** Ambar tarafı önceki dersteki kurulumdur: hat çıkışı `donem` anahtarıyla bölümlenmiş,
  sorgu iki sütun seçiyor. **Dosya sabiti 120 bir seçimdir.**
- **DY13.** **İşlenen satır** ayrı bir kalemdir: bir aşamanın gördüğü satır sayısı. Ambar bu bedeli
  yazma anında bir kez, göl her okumada yeniden öder. Süre ham olarak yazılmaz.
- **DY14.** Altı bozulma sınıfı Veri Mühendisliğine Giriş kursundan **girdi** alınır ve yakalama
  gecikmesi tablosu **tekrarlanmaz**; burada sorulan yeni soru bir bozulmanın görülmesi için
  **hangi kapsamın** gerektiğidir.
- **DY15.** Çözünürlük: 2200 satırda satır oranının adımı **0,0005**. Maliyet oran değildir; değerin
  kendisi yazılır.

#### Ham Katmanda Yazmak ve Okumak

Gölün yazma tarafı tek cümleyle tanımlanır: gelen satır ne ise o yazılır. Ambarın yazma tarafı ise
iki iş yapar; satırları hattan geçirir ve çıkan satırları seçilmiş düzene yerleştirir. Aşağıdaki kod
aynı kaynağı iki tarafa da verir ve tek bir soruyu, yani bir dönemin bölge toplamını, ikisine de
sorar.

```python
# KURGU depolama -- gercek bir gol urunu, dosya bicimi ya da sorgu motoru cagrilmaz.
# Kaynak bir olcum aginin KURGU sayac okumalaridir, tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


SUTUN = ("abone", "donem", "bolge", "tip", "endeks", "birim", "m3")
DOSYA_SABIT = 120          # her dosyaya dokunmanin sabit bedeli , hucre cinsinden


def yaz(kayitlar, bolumleme=()):
    """Kayitlari bolumleme anahtarina gore dosyalara dagitir."""
    duzen = {}
    for k in kayitlar:
        anahtar = tuple(k[a] for a in bolumleme)
        duzen.setdefault(anahtar, []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None, sutunlu=True):
    """Suzgec: {alan: deger}. Bolumleme anahtarindaki alanlar dosya budar ,
    obur alanlar dosyanin tamamini okutur."""
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    kalan = {a: v for a, v in suzgec.items() if a not in bolumleme}
    dosya, hucre, satir = 0, 0, 0
    okunan_sutun = len(secilen) if sutunlu else len(SUTUN)
    for anahtar, kayit in duzen.items():
        if budayan:
            eslesme = dict(zip(bolumleme, anahtar))
            if any(eslesme[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * okunan_sutun
        satir += sum(1 for k in kayit if all(k[a] == v for a, v in kalan.items()))
    return {"dosya": dosya, "hucre": hucre, "eslesen_satir": satir,
            "maliyet": dosya * DOSYA_SABIT + hucre}


# ---- HAM KATMAN: sema yok , bolumleme yok , sutun secilemez
def ham_oku(kayitlar):
    """Alan kumesi bilinmedigi icin her satirin butun alanlari okunur."""
    hucre = sum(len(k) for k in kayitlar)
    return {"dosya": 1, "hucre": hucre, "maliyet": 1 * DOSYA_SABIT + hucre}


def hattan_gecir(kayitlar):
    """Uc asama; her asamanin gordugu satir islenen olarak sayilir."""
    islenen, v = 0, kayitlar
    for f in (a1_ayikla, a2_tekille, a3_tuketim):
        islenen += len(v)
        v, _ = f(v)
    return v, islenen


kaynak = kaynak_uret()
gol = ham_oku(kaynak)
ambar_satir, donusum = hattan_gecir(kaynak)
duzenli = yaz(ambar_satir, ("donem",))
amb = sorgu(duzenli, ("donem",), ("bolge", "m3"), {"donem": "2026-07"})
SATIR = [("yazilan satir", len(kaynak), len(ambar_satir)),
         ("yazilan hucre", gol["hucre"], sum(len(k) for k in ambar_satir)),
         ("yazma aninda islenen satir", 0, donusum),
         ("sorgu: dokunulan dosya", gol["dosya"], amb["dosya"]),
         ("sorgu: okunan hucre", gol["hucre"], amb["hucre"]),
         ("sorgu: maliyet", gol["maliyet"], amb["maliyet"]),
         ("okuma aninda islenen satir", donusum, 0)]
print(f"{'olcu':<28}{'veri golu':>11}{'veri ambari':>13}")
for ad, a, b in SATIR:
    print(f"{ad:<28}{a:>11}{b:>13}")
print("yazmada golun kazanci", SATIR[1][2] - SATIR[1][1],
      " tek sorguda ambarin kazanci", gol["maliyet"] - amb["maliyet"])
```

```
olcu                          veri golu  veri ambari
yazilan satir                      2400         2200
yazilan hucre                     14400        15400
yazma aninda islenen satir            0         7200
sorgu: dokunulan dosya                1            1
sorgu: okunan hucre               14400          400
sorgu: maliyet                    14520          520
okuma aninda islenen satir         7200            0
yazmada golun kazanci 1000  tek sorguda ambarin kazanci 14000
```

Tablonun üst yarısı gölün iddiasını doğruluyor. Göl 2400 satırı 14400 hücreye yazıyor ve yazma
sırasında **hiçbir satır işlemiyor**; ambar 2200 satırı 15400 hücreye yazıyor ve bunun için hattın
üç aşamasında toplam **7200 satır** işliyor. Yazma tarafında gölün kazancı iki kalemdir: 1000 hücre
ve 7200 satırlık dönüşüm.

Alt yarı bu kazancı geri alıyor. Aynı soru gölde **14400 hücre** okutuyor, ambarda **400**; maliyet
**14520**'ye karşı **520**. Fark üç ayrı nedenden gelir ve üçü de şemanın yokluğundan doğar. Alan
kümesi bilinmediği için ham katmanda **sütun seçilemez**; hangi satırın hangi dönemde olduğu
düzenden okunamadığı için **bölüm budanamaz**; ve tüketim hesaplanmamış olduğu için hat **okuma
anında** yeniden koşar, yani 7200 satır her sorguda yeniden işlenir.

Son satır iki kazancı aynı birimde karşılaştırıyor: gölün yazmada kazandığı **1000 hücre**, tek bir
sorguda **14000 hücre** olarak geri ödeniyor. Bu modelde ambar kendini **ilk sorgudan önce**
amorti ediyor ve sorgu sayısı arttıkça fark doğrusal büyüyor. Buradan çıkan sonuç gölün gereksiz
olduğu değildir; gölün savunmasının **maliyet tablosunda olmadığıdır**. Göl, şeması henüz
bilinmeyen veriyi kabul edebildiği ve hiçbir satırı reddetmediği için tercih edilir. Bu iki
özelliğin sayısı bir sonraki tabloda.

#### Yazma Anının Göremediği

Bir bozulmanın görülebilmesi için ne kadar veriye bakmak gerektiği, o bozulmanın yazma anında
yakalanıp yakalanamayacağını belirler. Yazma anındaki bir kapı **tek satır** görür: elindeki satırı
kabul eder ya da reddeder. Bütün kümeye ya da özete bakan bir denetim yazma anında koşamaz, çünkü
küme henüz tamamlanmamıştır. Aşağıdaki kod altı sınıfı bu üç kapsama göre ayırıyor.

```python
# ---- KURGU bozulma siniflari. Hicbiri saldiri degildir; hepsi uretici tarafi kazasidir.
BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                # 60 kayit iki kez geldi
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI , sessiz bozulma
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":               # bir bolgenin bolumu hic yazilmadi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])          # sayi metne dondu
            sayac += 1
            yeni.append(y)
    return yeni, sayac


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


# ---- UC KAPSAM: satir , kume , ozet
SEMA = {"abone": str, "donem": str, "bolge": str, "tip": str, "endeks": int, "birim": str}


def satir_kapsami(k):
    """Yazma aninda tek satira bakilarak gorulebilen: alan ve tur."""
    return int(any(a not in k or not isinstance(k[a], t) for a, t in SEMA.items()))


def kume_kapsami(veri):
    """Butun kume elde olmadan gorulemeyen: tekillik , satir sayisi , bolum tamligi."""
    ad = [(k["abone"], k["donem"]) for k in veri]
    tekillik = len(ad) != len(set(ad))
    sayi = abs(len(veri) - 2400) / 2400 > 0.05
    gor = set((k["bolge"], k["donem"]) for k in veri)
    bolum = gor != set((b, d) for b in BOLGE for d in DONEM)
    return int(tekillik or sayi or bolum)


def ozet_kapsami(ozet, taban, pay=0.25):
    for ad, v in ozet.items():
        t = taban.get(ad)
        if t and abs(v - t) / max(abs(t), 1) > pay:
            return 1
    return 0


taban_ozet, _ = a4_ozet(ambar_satir)
print(f"{'sinif':<15}{'gelen':>7}{'satir':>7}{'kume':>6}{'ozet':>6}  "
      f"{'kapsam':<8}{'golde':>7}{'ambarda':>9}")
for s in ["temiz"] + BOZULMA:
    gelen, _ = (kaynak, 0) if s == "temiz" else boz(kaynak, s)
    sa = sum(satir_kapsami(k) for k in gelen)
    ku = kume_kapsami(gelen)
    oz = ozet_kapsami(a4_ozet(hattan_gecir(gelen)[0])[0], taban_ozet)
    kapsam = "satir" if sa else ("kume" if ku else ("ozet" if oz else "-"))
    print(f"{s:<15}{len(gelen):>7}{sa:>7}{ku:>6}{oz:>6}  "
          f"{kapsam:<8}{len(gelen):>7}{len(gelen) - sa:>9}")


# ---- Ayni gol katmanini iki okur farkli yorumluyor
def okur_onarimli(kayitlar):
    """Alan adi esleme tablosu tasiyan okur: eski ad yeni adla karsilanir."""
    y = []
    for k in kayitlar:
        if "endeks" not in k and "endeks_degeri" in k:
            k = dict(k)
            k["endeks"] = k.pop("endeks_degeri")
        y.append(k)
    return y


bozuk, _ = boz(kaynak, "sema_degisti")
for ad, veri in [("onarimsiz okur", bozuk), ("onarimli okur", okur_onarimli(bozuk))]:
    v, _ = hattan_gecir(veri)
    oz, _ = a4_ozet(v)
    print(f"{ad:<16}satir {len(v):>5}  ozet hucre {len(oz):>3}  toplam m3 {sum(oz.values()):>6}")
```

```
sinif            gelen  satir  kume  ozet  kapsam    golde  ambarda
temiz             2400      0     0     0  -          2400     2400
gec_gelen         2360      0     0     1  ozet       2360     2360
yinelenen         2460      0     1     0  kume       2460     2460
sema_degisti      2400    200     0     0  satir      2400     2200
birim_degisti     2400      0     0     1  ozet       2400     2400
eksik_bolum       2354      0     1     0  kume       2354     2354
kayan_tip         2400    200     0     0  satir      2400     2200
onarimsiz okur  satir  1800  ozet hucre  45  toplam m3  48004
onarimli okur   satir  2200  ozet hucre  55  toplam m3  58454
```

Kapsam sütunu dersin ana sonucunu taşıyor. Altı sınıfın yalnız **ikisi** tek satıra bakılarak
görülebiliyor; ikisi bütün kümeyi, ikisi de özeti gerektiriyor. Yani **yazma anındaki hiçbir satır
kapısı dördünü göremez** ve bu bir uygulama eksikliği değil, kapsamın kendisinden gelen bir
sınırdır: gelmeyen satır bir kapıdan geçmez, ikinci kez gelen satırın tekrar olduğu ancak birincisi
hatırlanırsa bilinir, bir bölgenin eksik dönemi ancak bütün dönemler yazıldıktan sonra görülür.

`birim_degisti` satırı en sessiz olanıdır: satır kapsamında 0, küme kapsamında da 0. Değer bin
katına çıkmış, birim etiketi `m3` kalmış ve satır şemaya kusursuz uyuyor; tekillik bozulmamış,
bölümler tam, satır sayısı doğru. Bu bozulmayı gören tek kapsam **özettir**, yani hat koşup
toplamlar çıktıktan sonrası. Depolama açısından sonucu şudur: bu sınıfa karşı ambarın yazma kapısı
ile gölün kapısızlığı arasında **hiçbir fark yoktur**; ikisi de 2400 satırı yazar.

Göl ile ambarın ayrıldığı yer son iki sütundur ve fark yalnız **iki satırdadır**: `sema_degisti` ile
`kayan_tip` gölde 2400, ambarda 2200 satır olarak duruyor. Gölün ödediği yakalama farkı bu iki
sınıftan ibarettir; buna karşılık gölde kalan 200 satır **kaybolmamıştır**. Son iki satır bunun
sonucunu gösteriyor: aynı göl katmanını okuyan iki tüketici farklı cevap üretiyor. Onarımsız okur
alan adını tanımadığı için 200 satırı düşürüyor, ardından bir sonraki dönemin farkı hesaplanamadığı
için 200 satır daha kaybediyor; elinde **1800 satır, 45 özet hücresi ve 48004 m3** kalıyor. Alan adı
eşlemesi taşıyan okur **2200 satır, 55 hücre ve 58454 m3** buluyor. Aradaki **10450 m3**, diskteki
baytların değil **okuma anındaki yorumun** farkıdır.

Kursun ikinci iddiası burada da geçerlidir: **düzen de bozar.** Ham düzen hiçbir satırı yok etmiyor,
buna karşılık doğru cevabın ne olduğunu **depolama katmanı söylemiyor**. Şema dayatmasını okuma
anına ertelemek onu ortadan kaldırmaz, yalnızca **çoğaltır**: her tüketici kendi şemasını taşır ve
tüketici sayısı kadar cevap oluşur.

#### Özet

- Veri gölü gelen veriyi olduğu gibi saklar; şema ortadan kalkmaz, okuma anına ertelenir ve okuyan
  taraf onu kendi taşır.
- Göl 2400 satırı 14400 hücreye hiçbir dönüşüm yapmadan yazıyor; ambar 2200 satırı 15400 hücreye
  yazmak için 7200 satır işliyor.
- Aynı dönem sorgusu gölde 14400 hücre ile 14520 maliyet, ambarda 400 hücre ile 520 maliyet veriyor;
  gölün yazmada kazandığı 1000 hücre tek bir sorguda 14000 olarak geri ödeniyor.
- Altı bozulma sınıfının ikisi satır, ikisi küme, ikisi özet kapsamında görülüyor; yazma anındaki
  bir satır kapısı dördünü göremez ve `birim_degisti` satır ile küme kapsamının ikisinde de
  görünmez.
- Göl ile ambarın yazma farkı yalnız iki sınıftadır; buna karşılık aynı göl katmanını okuyan iki
  tüketici 1800 ve 2200 satır, 48004 ve 58454 m3 buluyor.

#### Sonraki Adım

İki ders iki uç kararı ölçtü. Ambar şemayı yazma anında dayatıyor, sorguya ucuz bir okuma veriyor ve
karşılığında kurtarılabilir satırı yok ediyor. Göl hiçbir satırı yok etmiyor, karşılığında her
sorguya hattı yeniden koşturuyor ve doğru yorumu tüketiciye bırakıyor. İki kararın istenen yanları
birbirini dışlamıyor gibi görünür: ham katman korunabilir **ve** şema dayatması yine de tek bir
yerde toplanabilir, yalnız yazma anında değil **okuma anında**. Sonraki ders bu birleşimi, yani
**göl evi yaklaşımını** ölçer. Sorusu tek bir sayıya bağlanır: dayatma okuma anına taşındığında
bozulma kaç aşama sonra yakalanıyor. Ölçü Veri Mühendisliğine Giriş kursunun birimiyle, yani
**aşama** olarak okunacak; kazanılanın yanına kaybedilenin de yazılabilmesi için saklanan hücre
ayrıca sayılacak.

### Göl Evi Yaklaşımı

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/gol-evi-yaklasimi

Ham katman korunurken şema dayatmasının okuma anına taşınmasının kazandırdığının ve kaybettirdiğinin sayılması: yazma kapısı altı bozulma sınıfının ikisini sıfır aşama gecikmeyle yakalıyor ve dördünü hiç yakalamıyor, hiçbir dayatması olmayan düzenek altısını da yakalamıyor, okuma anında dayatan düzenek altısını da yakalıyor ve ortalama gecikmesi 1,33 aşama oluyor. Ortalamanın yükselmesi kötüleşme değil, tanımsız dört değerin sonlu hâle gelmesidir. Alan adı değişimi altında ham katmanı koruyan düzenek 2200 düzenli satır üretiyor, kapıda eleyen düzenek 200 satırı reddedip 1800'de kalıyor; bedel saklanan hücrenin 12600 yerine 29800 olmasıdır. Birim değişimi hiçbir kapıya takılmadan düzenli katmana yazılıyor ve ancak dördüncü aşamada görülüyor.

Önceki iki ders iki uç kararı ölçtü. Veri ambarı şemayı yazma anında dayatıyor, sorguya 520
maliyetli bir okuma veriyor ve karşılığında kurtarılabilir 400 satırı yok ediyor. Veri gölü hiçbir
satırı yok etmiyor, karşılığında aynı sorguya 14520 ödetiyor ve doğru yorumu tüketiciye bırakıyor;
aynı ham katmanı okuyan iki tüketicinin 48004 ile 58454 metreküp bulması bunun sonucuydu.

**Göl evi (lakehouse)** bu iki kararı ayırıp yeniden birleştirir. Ham katman gölde olduğu gibi
korunur: gelen satır ne ise o yazılır, hiçbir şey reddedilmez. Şema dayatması ortadan kalkmaz, ama
yazma anından alınıp **okuma anına** taşınır ve tek bir yerde toplanır; sonucu ambarınki gibi
şemaya uyumlu ve bölümlenmiş bir **düzenli katmandır**. Bu birleşimin kazandırdığı ve kaybettirdiği
tek bir eksende okunur: **yakalama gecikmesi**. Ölçü Veri Mühendisliğine Giriş kursunun birimiyle,
yani **aşama** olarak yazılır, böylece üç düzenek aynı cetvelle karşılaştırılabilir.

- **DY16.** Kod bir **kurgudur**; hiçbir göl evi ürünü, tablo biçimi, katalog ya da sorgu motoru adı
  yazılmaz. Kaynak, hat ve denetimler Veri Mühendisliğine Giriş kursundan devralınır, tohum
  **20260218**.
- **DY17.** Göl evi iki katmanlıdır: **ham katman** gelen satırı olduğu gibi tutar, **düzenli
  katman** şema dayatılmış hat çıkışını tutar. Dayatma düzenli katman kurulurken, yani **okuma
  anında** koşar.
- **DY18.** Yakalama gecikmesi **aşama** olarak yazılır: **0** hiçbir aşama koşmadan, **1** ile
  **5** beş aşamanın sırası. Süre ham olarak yazılmaz.
- **DY19.** Yedi denetim ve altı bozulma sınıfı Veri Mühendisliğine Giriş kursundan **aynen** alınır
  ve yordamları **tekrarlanmaz**. Bu dersin yeni sorusu tek satırlıktır: dayatma okuma anına
  taşındığında gecikme ne oluyor.
- **DY20.** Ambarın yazma kapısı yalnız **satır kapsamı** görür, yani `sema` ile `tip`; küme ve özet
  kapsamındaki denetimler yazma anında koşamaz. Bu ayrım önceki derste ölçüldü.
- **DY21.** Ham katman korunduğu için **onarım** mümkündür: alan adı eşlemesi ve tür çevrimi. Ambar
  kapısında onarım yoktur; uymayan satır reddedilir.
- **DY22.** Sorgu maliyeti bu derste yeniden ölçülmez, önceki iki dersten **girdi** alınır: ham
  katmanda 14520, `donem` bölümlü düzenli katmanda 520. **Dosya sabiti 120 bir seçimdir.**

#### Dayatmayı Okuma Anına Taşımak

Üç düzenek aynı bozuk kaynağı görür ve yalnız **hangi denetimin nerede koştuğu** bakımından
ayrılır. Ambarda yazma kapısı yalnız satır kapsamındaki iki denetimi işletir. Gölde hiçbir denetim
yoktur. Göl evinde yedi denetimin tamamı okuma anında, düzenli katman kurulurken koşar ve her biri
kendi aşamasında ateşler.

```python
# KURGU hat ve denetimler -- gercek bir gol evi urunu, tablo bicimi ya da sorgu
# motoru cagrilmaz. Kaynak bir olcum aginin KURGU sayac okumalaridir, tohum 20260218.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


BOZULMA = ["gec_gelen", "yinelenen", "sema_degisti", "birim_degisti", "eksik_bolum", "kayan_tip"]


def boz(kayit, sinif, donem="2026-07"):
    """Tek bir donemi bozar , kalan donemler saglam kalir."""
    yeni, sayac = [], 0
    for k in kayit:
        if k["donem"] != donem:
            yeni.append(k)
            continue
        if sinif == "gec_gelen":
            if k["abone"] <= "A040":                # bu donemde 40 abone hic gelmedi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "yinelenen":
            yeni.append(k)
            if k["abone"] <= "A060":                # 60 kayit iki kez geldi
                yeni.append(dict(k))
                sayac += 1
        elif sinif == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        elif sinif == "birim_degisti":
            y = dict(k)
            y["endeks"] = k["endeks"] * 1000        # m3 yerine litre
            y["birim"] = "m3"                       # ETIKET DEGISMEDI , sessiz bozulma
            sayac += 1
            yeni.append(y)
        elif sinif == "eksik_bolum":
            if k["bolge"] == "kuzey":               # bir bolgenin bolumu hic yazilmadi
                sayac += 1
                continue
            yeni.append(k)
        elif sinif == "kayan_tip":
            y = dict(k)
            y["endeks"] = str(k["endeks"])          # sayi metne dondu
            sayac += 1
            yeni.append(y)
    return yeni, sayac


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    """Donem farkindan tuketim. Onceki donem yoksa satir hesaplanamaz."""
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


def a4_ozet(kayit):
    top = {}
    for k in kayit:
        ad = (k["bolge"], k["donem"])
        top[ad] = top.get(ad, 0) + k["m3"]
    return top, 0


def a5_sun(ozet):
    """Sunum: bolge basina donem ortalamasi."""
    bolge = {}
    for (b, d), v in ozet.items():
        bolge.setdefault(b, []).append(v)
    return {b: round(sum(v) / len(v), 2) for b, v in bolge.items()}, 0


ASAMA = [("ayikla", a1_ayikla), ("tekille", a2_tekille), ("tuketim", a3_tuketim),
         ("ozet", a4_ozet), ("sun", a5_sun)]


# ---- DENETIMLER: her biri belirli bir asamadan SONRA kosar
def d_sema(veri, alanlar=("abone", "donem", "bolge", "endeks", "birim")):
    return any(any(a not in k for a in alanlar) for k in veri)


def d_tip(veri):
    return any(not isinstance(k.get("endeks"), int) for k in veri)


def d_tekillik(veri):
    ad = [(k["abone"], k["donem"]) for k in veri]
    return len(ad) != len(set(ad))


def d_satir_sayisi(veri, beklenen, pay=0.05):
    return abs(len(veri) - beklenen) / beklenen > pay


def d_bolum_tamligi(veri):
    """KUSURLU DENETIM: bolge kumesine bakar , donemi gormez."""
    return set(k["bolge"] for k in veri) != set(BOLGE)


def d_bolum_tamligi_donem(veri):
    """DOGRU DENETIM: her donemde her bolge var mi."""
    gor = set((k["bolge"], k["donem"]) for k in veri)
    return gor != set((b, d) for b in BOLGE for d in DONEM)


def d_dagilim(ozet, taban, pay=0.25):
    for ad, v in ozet.items():
        t = taban.get(ad)
        if t and abs(v - t) / max(abs(t), 1) > pay:
            return True
    return False


DENETIM = [
    ("sema", 0, lambda v, t: d_sema(v)),
    ("tip", 0, lambda v, t: d_tip(v)),
    ("tekillik", 0, lambda v, t: d_tekillik(v)),
    ("satir_sayisi", 0, lambda v, t: d_satir_sayisi(v, t["beklenen_satir"])),
    ("bolum", 0, lambda v, t: d_bolum_tamligi(v)),
    ("bolum_donem", 0, lambda v, t: d_bolum_tamligi_donem(v)),
    ("dagilim", 4, lambda v, t: d_dagilim(v, t["taban_ozet"])),
]


def denetimli_hat(kayit, taban, acik=None):
    """Hatti kosturur ve her asamadan sonra acik denetimleri isletir.
    Donen deger: sunum , iz , ilk yakalayan denetim ve yakalama gecikmesi (asama)."""
    acik = [a for a, _, _ in DENETIM] if acik is None else acik
    veri, iz, yakalayan, gecikme = kayit, [], None, None
    for adim in range(len(ASAMA) + 1):
        if adim > 0:
            ad, f = ASAMA[adim - 1]
            veri, dusen = f(veri)
            iz.append({"asama": ad, "kalan": len(veri), "dusen": dusen})
        for dad, dasama, df in DENETIM:
            if dad not in acik or dasama != adim:
                continue
            if df(veri, taban):
                if yakalayan is None:
                    yakalayan, gecikme = dad, adim
    sunum = veri if isinstance(veri, dict) and veri and isinstance(
        list(veri.values())[0], float) else None
    return sunum, iz, yakalayan, gecikme


def taban_kur(kaynak):
    v1, _ = a1_ayikla(kaynak)
    v2, _ = a2_tekille(v1)
    v3, _ = a3_tuketim(v2)
    oz, _ = a4_ozet(v3)
    sun, _ = a5_sun(oz)
    return {"beklenen_satir": len(kaynak), "taban_ozet": oz, "taban_sunum": sun}


kaynak = kaynak_uret()
taban = taban_kur(kaynak)
DUZENEK = [("veri ambari", ["sema", "tip"]),        # kapi yalnizca satira bakar
           ("veri golu", []),                       # hicbir dayatma yok
           ("gol evi", None)]                       # butun denetimler okuma aninda


def yaz_hucre(y, g):
    return f"{y}/{g}" if y else "-"


gecikme_kaydi = {ad: [] for ad, _ in DUZENEK}
print(f"{'sinif':<15}{'veri ambari':>14}{'veri golu':>11}{'gol evi':>16}")
for s in BOZULMA:
    gelen, _ = boz(kaynak, s)
    hucre = []
    for ad, acik in DUZENEK:
        _, _, y, g = denetimli_hat(gelen, taban, acik=acik)
        if y:
            gecikme_kaydi[ad].append(g)
        hucre.append(yaz_hucre(y, g))
    print(f"{s:<15}{hucre[0]:>14}{hucre[1]:>11}{hucre[2]:>16}")
for ad, _ in DUZENEK:
    v = gecikme_kaydi[ad]
    ort = f"{sum(v) / len(v):.2f}" if v else "tanimsiz"
    print(f"{ad:<15}yakalanan {len(v)}/{len(BOZULMA)}  ortalama gecikme {ort}")
```

```
sinif             veri ambari  veri golu         gol evi
gec_gelen                   -          -       dagilim/4
yinelenen                   -          -      tekillik/0
sema_degisti           sema/0          -          sema/0
birim_degisti               -          -       dagilim/4
eksik_bolum                 -          -   bolum_donem/0
kayan_tip               tip/0          -           tip/0
veri ambari    yakalanan 2/6  ortalama gecikme 0.00
veri golu      yakalanan 0/6  ortalama gecikme tanimsiz
gol evi        yakalanan 6/6  ortalama gecikme 1.33
```

Son sütun Veri Mühendisliğine Giriş kursunun ilk yakalayan denetim sütununun **birebir aynısıdır**;
göl evi düzeneği o kursun tam denetimli hattıyla aynı sonucu veriyor, çünkü dayatmanın **nerede**
durduğu değişse de denetim kümesi değişmiyor. Karşılaştırma bu yüzden geçerlidir: üç sütun aynı
cetvelle, aşama birimiyle okunuyor.

Kazanç kapsam tarafındadır. Yazma kapısı **2/6**, hiçbir dayatması olmayan düzenek **0/6**, okuma
anında dayatan düzenek **6/6** yakalıyor. Kapının yakalayamadığı dört sınıf yalnız gözden kaçmıyor;
kapı onları **görebilecek konumda değil**, çünkü ikisi küme, ikisi özet kapsamı gerektiriyor ve bu
kapsamlar yazma anında henüz oluşmamıştır.

Ortalama gecikme satırı dikkatle okunmalıdır. Ambarın **0,00** değeri göl evinin **1,33** değerinden
küçüktür, ama bu ambarın daha hızlı yakaladığı anlamına gelmez. İki ortalama **farklı sayıda sınıf
üzerinden** alınmıştır: ambarınki yalnız yakaladığı iki sınıfın, göl evininki altı sınıfın
ortalamasıdır. Göl evinin yakaladığı iki ortak sınıf, yani `sema_degisti` ile `kayan_tip`, orada da
**0 aşamada** yakalanıyor; dayatmanın okuma anına taşınması bu ikisini **hiç geciktirmiyor**.
Ortalamanın 0,00'dan 1,33'e çıkmasının tek nedeni, ambarda **tanımsız** olan dört değerin sonlu hâle
gelmesidir: 0, 0, 4 ve 4. Bu, Veri Mühendisliğine Giriş kursundaki ayrımın depolama tarafındaki
karşılığıdır: **tanımsız bir gecikme sıfırdan iyi değildir, ölçülmemiş olandır.** Bir düzeneğin
ortalama gecikmesi, kaç sınıfı hiç yakalamadığı yazılmadan okunamaz.

#### Ham Katmanın Kurtardığı ve Ödettiği

Gecikme tablosu iki düzeneğin `sema_degisti` karşısında aynı sonucu verdiğini gösteriyor: ikisi de
sıfır aşamada yakalıyor. Ayrıldıkları yer **yakaladıktan sonra ne yaptıklarıdır**. Ambar satırı
reddeder, göl evi ham katmanda tutar ve okuma anında onarır.

```python
SEMA = {"abone": str, "donem": str, "bolge": str, "tip": str, "endeks": int, "birim": str}
ESLEME = {"endeks_degeri": "endeks"}


def uymayan(k):
    return any(a not in k or not isinstance(k[a], t) for a, t in SEMA.items())


def kapida_ele(kayitlar):
    """Ambar: uymayan satir hic yazilmaz."""
    kabul = [k for k in kayitlar if not uymayan(k)]
    return kabul, len(kayitlar) - len(kabul)


def okuma_aninda_onar(kayitlar):
    """Gol evi: ham katman korunur , dayatma okuma aninda onarimla uygulanir."""
    y = []
    for k in kayitlar:
        if uymayan(k):
            k = dict(k)
            for eski, yeni in ESLEME.items():
                if eski in k and yeni not in k:
                    k[yeni] = k.pop(eski)
            if isinstance(k.get("endeks"), str) and k["endeks"].isdigit():
                k["endeks"] = int(k["endeks"])
        y.append(k)
    return y


def hattan(kayitlar):
    v = kayitlar
    for f in (a1_ayikla, a2_tekille, a3_tuketim):
        v, _ = f(v)
    return v


def hucre(kayitlar):
    return sum(len(k) for k in kayitlar)


bozuk, _ = boz(kaynak, "sema_degisti")
kabul, red = kapida_ele(bozuk)
onarilmis = okuma_aninda_onar(bozuk)
print("sema_degisti altinda uc duzenek")
print(f"{'duzenek':<14}{'reddedilen':>11}{'ham katman':>12}{'duzenli satir':>15}"
      f"{'saklanan hucre':>16}")
for ad, ham, duzenli_kaynak in [("veri golu", bozuk, bozuk),
                                ("veri ambari", [], kabul),
                                ("gol evi", bozuk, onarilmis)]:
    d = hattan(duzenli_kaynak)
    h = hucre(ham) + (hucre(d) if ad != "veri golu" else 0)
    print(f"{ad:<14}{red if ad == 'veri ambari' else 0:>11}{len(ham):>12}{len(d):>15}{h:>16}")
temiz = hattan(kaynak)
print("temiz kaynakta saklanan hucre  gol", hucre(kaynak), " ambar", hucre(temiz),
      " gol evi", hucre(kaynak) + hucre(temiz))
```

```
sema_degisti altinda uc duzenek
duzenek        reddedilen  ham katman  duzenli satir  saklanan hucre
veri golu               0        2400           1800           14400
veri ambari           200           0           1800           12600
gol evi                 0        2400           2200           29800
temiz kaynakta saklanan hucre  gol 14400  ambar 15400  gol evi 29800
```

Kazandırdığı üçüncü sütunda: göl evi **2200** düzenli satır üretiyor, öbür iki düzenek **1800**.
Dört yüz satırlık farkın yarısı doğrudan reddedilen kayıtlardır, öteki yarısı ise onların
dolaylı sonucudur; alan adı değişen dönem düştüğünde bir sonraki dönemin tüketim farkı da
hesaplanamaz hâle gelir. Ambarın kaybı bu yüzden reddettiği satır sayısından **büyüktür** ve bu
büyümeyi kapının kendisi bildirmez. Göl evinde ham katman durduğu için eşleme tablosu yazıldığı anda
her iki dönem de geri gelir.

Kaybettirdiği son sütunda: göl evi **29800** hücre saklıyor, ambar **12600**, göl **14400**. Temiz
kaynakta oran daha nettir: 14400, 15400 ve 29800. Göl evi ikisinin toplamını saklar, çünkü aynı
veriyi hem ham hem düzenli biçimde tutar. Sorgu maliyetinde ambarın seviyesindedir, yani 520; ham
katmanı da 14520 ile sorgulanabilir. Yani göl evinin aldığı şey **saklama hücresi karşılığında
alınmış bir seçenektir**: düzenli katman bozulduğunda ya da şema anlayışı değiştiğinde yeniden
üretilebilir.

Kursun ikinci iddiası burada da yazılır: **düzen de bozar.** Göl evinin hiçbir satırı reddetmemesi,
düzenli katmanının temiz olduğu anlamına gelmez. `birim_degisti` satırı bunun kanıtıdır: kapıya
takılmıyor, onarım gerektirmiyor, düzenli katmana **2200 satırın tamamı** yazılıyor ve bozulma
ancak **dördüncü aşamada** görülüyor. O noktaya kadar düzenli katman kusursuz görünen bir şemaya
sahip, tam sayıda satır taşıyan ve iki dönemi bin katına çıkmış bir tablodur. Şemaya uymak doğru
olmakla aynı şey değildir; okuma anına taşınan dayatma bu farkı kapatmaz.

#### Özet

- Göl evi ham katmanı korur ve şema dayatmasını yazma anından okuma anına taşıyarak tek bir yerde
  toplar; sonucu şemaya uyumlu bir düzenli katmandır.
- Yakalanan bozulma sınıfı yazma kapısında 2/6, hiçbir dayatması olmayan düzenekte 0/6, okuma anında
  dayatan düzenekte 6/6.
- Ortalama yakalama gecikmesi ambarda 0,00 ve göl evinde 1,33 aşamadır; ortak iki sınıf ikisinde de
  0 aşamada yakalandığına göre ortalamanın yükselmesi kötüleşme değil, tanımsız dört değerin sonlu
  hâle gelmesidir.
- Alan adı değişimi altında göl evi 2200 düzenli satır üretiyor, öbür iki düzenek 1800; kaybın
  yarısı reddedilen satır, yarısı onların düşürdüğü bir sonraki dönemdir.
- Bedel saklanan hücredir: temiz kaynakta göl 14400, ambar 15400, göl evi 29800.
- Okuma anında dayatma bozuk satırın düzenli katmana yazılmasını engellemez; `birim_degisti` hiçbir
  kapıya takılmadan 2200 satır olarak yazılıyor ve dördüncü aşamada görülüyor.

#### Sonraki Adım

Üç ders depolamanın **ne sakladığını** birbirinden ayırdı. Ambar şemaya uyumlu satırı, göl gelen
satırın kendisini, göl evi ikisini birden saklıyor ve her seçimin sorguya, yakalamaya ve saklanan
hücreye ödettiği ayrı ayrı yazıldı. Üç derste hiç sorulmayan bir soru var: bu satırlar **kimin
için** saklanıyor. Üç düzeneğin de tek bir tüketiciye hizmet ettiği varsayıldı, oysa aynı ambardan
okuyan taraflar farklı sorular sorar ve farklı sütunlara bakar. Bir bölgeyle ilgilenen taraf bütün
bölgeleri taşıyan bir düzeni her sorguda baştan süzmek zorunda kalır. Sonraki ders bu soruyu
sorar ve alan odaklı alt kümeleri, yani **veri pazarlarını** ölçer; ölçüsü iki sayının yan yana
durmasıdır: alt küme ayrı yazıldığında sorgu maliyeti ne oluyor ve **aynı satır kaç yerde duruyor**.

### Veri Pazarı

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/veri-pazari

Tek bir birime adanmış bir alt küme ayrı yazıldığında sorgu maliyetinin ne olduğunun ve aynı satırın kaç yerde durduğunun sayılması: üç soruluk bir kümede bölümlemesiz ambar 13.560, en iyi merkezî düzen 3.256, iki pazar 2.580 ödüyor ve pazarın kazancı bölge sorusunda sıfır, tip sorusunda 676. Bedel çoğalmadır: altı pazar 3.322 satır daha saklıyor, satır başına 2,51 kopya, 1.122 satır üç ayrı yerde duruyor. Geç gelen kayıt ambara işlendiğinde yenilenmemiş altı pazar 126 (satır, yer) çiftinde ayrışıyor ve yarı yenilenmiş bir kümede beş bölge pazarının toplamı 4.707, ambarınki 5.340 çıkıyor.

Önceki üç ders depolamanın **ne sakladığını** ayırdı: şemaya uyan satırı, ham satırı ve ikisini
aynı yerde tutan düzeni. Üçünde de sorulan soru "bu satır nasıl saklanır" idi ve yanıt bir yazma
kararıydı. Bir soru hiç sorulmadı: bu satır **kimin için** saklanır. Bölge sorumlusunun sorduğu
soru ile tarife sorumlusunun sorduğu soru aynı satırlara dokunur, fakat aynı düzeni istemez.

Bu dersin ölçtüğü şey o ayrışmanın bedelidir. Tek bir birimin sorularına adanmış alt küme ambardan
çıkarılıp ayrı yazıldığında sorgu maliyeti düşer; karşılığında aynı satır birden çok yerde durur
ve o yerler ayrı ayrı tazelenir. İki sayı yan yana konur: alt kümenin kazandırdığı maliyet ve
**aynı satırın kaç kopyası olduğu**.

- **DY41.** Kaynak, hat ve depolama düzeni **kurgudur** ve ders içinde tanımlanır. Hiçbir ambar,
  göl, dosya biçimi, katalog ürünü ya da sorgu motoru çağrılmaz. Tohum 20260218.
- **DY42.** **Veri pazarı (data mart)**, ambardaki satırların tek bir alan değeri üzerinden
  çıkarılmış ve **ayrı yazılmış** alt kümesidir. Türetme kuralı tek alan üzerinde eşitliktir;
  özetleme yoktur, satırlar ambardaki satırların aynısıdır.
- **DY43.** Maliyet ölçüsü kurs boyunca aynıdır: `dosya x 120 + okunan hücre`. **Dosya sabiti 120
  bir seçimdir**; bu derste sabit tutulur, süpürülmesi sonraki derse bırakılır.
- **DY44.** Bir pazar **yazıldığı anın satırlarını** taşır. Tazeleme ayrı bir iştir ve ambarın
  tazelenmesiyle aynı anda olmak zorunda değildir.
- **DY45.** Bölümleme, parçalama ve çoğaltma yordamı Veritabanları müfredatındaki İlişkisel
  Veritabanı Yönetimi kursunda ve Sistem Tasarımı ve Dağıtık Sistemler müfredatındaki Veri Katmanı
  Ölçekleme kursunda ölçüldü; **burada tekrarlanmaz**. Bu ders yalnız **alt küme çıkarma kararının
  sorguya ödettiğini** sayar.
- **DY46.** Satır oranı basılacaksa çözünürlük yazılır: küme 2.200 satırdır, oran adımı 0,0005.
  Maliyet bir orandır değil **tam sayıdır** ve değerin kendisi yazılır.

#### Alt Kümenin Kattığı

Ölçüm üç soruyla yapılır. İkisi bir bölgenin sorusudur: o bölgenin bir dönemi ve o bölgenin bütün
dönemleri. Üçüncüsü bir tarife sorusudur: bir dönemdeki işyeri aboneleri. Üç soru da aynı 2.200
satırın üstündedir ve üçü de aynı yanıtı vermek zorundadır; ayrıldıkları tek yer, o yanıta ulaşmak
için dokunulan dosya ve okunan hücredir.

```python
# KURGUDUR. Olcum aginin sayac okumalari, hat ve depolama duzeni ders icinde
# tanimlanir; hicbir ambar, gol, dosya bicimi ya da sorgu motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


SUTUN = ("abone", "donem", "bolge", "tip", "endeks", "birim", "m3")
DOSYA_SABIT = 120          # bir dosyaya dokunmanin sabit bedeli , hucre cinsinden


def hattan(kaynak):
    v, _ = a1_ayikla(kaynak)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def yaz(kayitlar, bolumleme=()):
    """Kayitlari bolumleme anahtarina gore dosyalara dagitir."""
    duzen = {}
    for k in kayitlar:
        duzen.setdefault(tuple(k[a] for a in bolumleme), []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None, sutunlu=True):
    """Bolumleme anahtarindaki alan dosya budar , obur alan dosyanin tamamini okutur."""
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    kalan = {a: v for a, v in suzgec.items() if a not in bolumleme}
    dosya, hucre, satir = 0, 0, 0
    okunan_sutun = len(secilen) if sutunlu else len(SUTUN)
    for anahtar, kayit in duzen.items():
        if budayan:
            es = dict(zip(bolumleme, anahtar))
            if any(es[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * okunan_sutun
        satir += sum(1 for k in kayit if all(k[a] == v for a, v in kalan.items()))
    return {"dosya": dosya, "hucre": hucre, "eslesen_satir": satir,
            "maliyet": dosya * DOSYA_SABIT + hucre}


def pazar(kayitlar, alan, deger):
    """Birim odakli alt kume: ambardan tek bir degere ait satirlar ayri yazilir."""
    return [k for k in kayitlar if k[alan] == deger]


TUM = hattan(kaynak_uret())
SEC = ("abone", "m3")
SORU = [("dogu/07", {"bolge": "dogu", "donem": "2026-07"}),
        ("dogu/tum", {"bolge": "dogu"}),
        ("isyeri/07", {"tip": "isyeri", "donem": "2026-07"})]
DUZEN = [("bolumlemesiz", ()), ("donem", ("donem",)), ("donem+bolge", ("donem", "bolge")),
         ("donem+bolge+tip", ("donem", "bolge", "tip"))]

print(f"hattan cikan satir {len(TUM)} , dosya sabiti {DOSYA_SABIT} hucre")
print("\nmerkezi ambar     dosya   " + "  ".join(f"{a:>10}" for a, _ in SORU) + "   toplam")
for ad, bl in DUZEN:
    d = yaz(TUM, bl)
    m = [sorgu(d, bl, SEC, f)["maliyet"] for _, f in SORU]
    print(f"{ad:<17} {len(d):>5}   " + "  ".join(f"{x:>10}" for x in m) + f"   {sum(m):>6}")

p_bolge = yaz(pazar(TUM, "bolge", "dogu"), ("donem",))
p_tip = yaz(pazar(TUM, "tip", "isyeri"), ("donem",))
m = [sorgu(p_bolge, ("donem",), SEC, {"donem": "2026-07"})["maliyet"],
     sorgu(p_bolge, ("donem",), SEC, {})["maliyet"],
     sorgu(p_tip, ("donem",), SEC, {"donem": "2026-07"})["maliyet"]]
print(f"{'iki pazar':<17} {len(p_bolge) + len(p_tip):>5}   "
      + "  ".join(f"{x:>10}" for x in m) + f"   {sum(m):>6}")
```

```
hattan cikan satir 2200 , dosya sabiti 120 hucre

merkezi ambar     dosya      dogu/07    dogu/tum   isyeri/07   toplam
bolumlemesiz          1         4520        4520        4520    13560
donem                11          520        5720         520     6760
donem+bolge          55          188        2068        1000     3256
donem+bolge+tip     110          308        3388         804     4500
iki pazar            22          188        2068         324     2580
```

Tablonun ilk satırı taban çizgisidir: bölümlemesiz, satırlı, tarihçesiz düzende üç soru da aynı tek
dosyayı baştan sona okur ve toplam 13.560 öder. Pazar kurulumu 2.580 ile en ucuzudur ve bunu
**yirmi iki dosyayla** yapar; en iyi merkezî düzen olan `donem`+`bolge` elli beş dosyayla 3.256
öder. Ancak asıl okunması gereken yer toplam değil, sütunlardır.

Bölge sorularında pazarın kazancı **sıfırdır**. `dogu/07` hem `donem`+`bolge` düzeninde hem doğu
pazarında 188, `dogu/tum` ikisinde de 2.068. Bunun nedeni açıktır: bir bölge pazarı, bölge alanını
bölüm anahtarına eklemekle **aynı budamayı** yapar. Aynı sonucu veren iki yoldan biri satırları
kopyalar, öbürü yalnızca dizilişi değiştirir. Bir alt küme çıkarma kararının bölümleme kararından
farklı bir şey kazandırdığı iddia ediliyorsa, o farkın sayısı gösterilmelidir.

Fark üçüncü sütunda görünür. `isyeri/07` sorusunda `donem`+`bolge` düzeni 1.000, işyeri pazarı 324
öder; kazanç 676. Merkezî düzen bu soruyu ucuzlatamaz, çünkü bölüm anahtarı **tek bir
sıralamadır** ve o sıralama bölgeye ayrılmıştır. Üçüncü alanı da anahtara eklemek işi düzeltmez,
kötüleştirir: `donem`+`bolge`+`tip` düzeni yüz on dosyaya çıkar ve toplamı 3.256'dan 4.500'e taşır,
üstelik ilk iki soruda da pahalılaşır. Okunan hücre azalırken dosya sabitinin yüz on kez ödenmesi
kazancı yutar. **Pazarın gerçek kazandırdığı şey budur: her alt kümenin kendi bölüm anahtarını
seçebilmesi.** Tek bir düzende bu mümkün değildir.

#### Aynı Satır Kaç Yerde Duruyor

Kazanç bir yazma kararıyla alındı ve her yazma kararı bir kopya üretti. Şimdi sayılacak olan
kopyanın kendisidir. Kurulum genişletilir: beş bölge pazarı ve bir tarife pazarı, ambarın yanında.
Ardından üretici tarafında bir kaza olur — bir dönemde kırk abonenin okuması geç gelir — ve kayıt
sonradan ambara işlenir. Pazarların tazelenmesi ayrı bir iştir; tazelenmedikleri sürece ne
söyledikleri sayılır.

```python
# Onceki blogun tanimlari surer: hattan, kaynak_uret, pazar, TUM, BOLGE.
def gec_gelen(kayit, donem="2026-07", ust="A040"):
    """KURGU uretici kazasi: bu donemde 40 abonenin okumasi hic gelmedi."""
    return [k for k in kayit if not (k["donem"] == donem and k["abone"] <= ust)]


def anahtar(kayitlar):
    return {(k["abone"], k["donem"]) for k in kayitlar}


def toplam(kayitlar, donem="2026-07"):
    return sum(k["m3"] for k in kayitlar if k["donem"] == donem)


PAZAR = [(b, "bolge", b) for b in BOLGE] + [("isyeri", "tip", "isyeri")]
ESKI = hattan(gec_gelen(kaynak_uret()))       # gec gelen kayitla yazilmis pazarlar

kopya = {}
for k in TUM:
    n = 1 + sum(1 for _, a, d in PAZAR if k[a] == d)
    kopya[n] = kopya.get(n, 0) + 1
saklanan = len(TUM) + sum(len(pazar(TUM, a, d)) for _, a, d in PAZAR)
print(f"ambar {len(TUM)} satir , alti pazar {saklanan - len(TUM)} satir , "
      f"saklanan toplam {saklanan}")
print(f"iki yerde duran satir {kopya[2]} , uc yerde duran {kopya[3]} , "
      f"satir basina ortalama {saklanan / len(TUM):.2f} kopya")

print("\npazar     satir   yenilenmemis  eksik satir   07 ambar   07 pazar   fark")
eksik_toplam = 0
for ad, a, d in PAZAR:
    yeni, esk = pazar(TUM, a, d), pazar(ESKI, a, d)
    eksik = len(anahtar(yeni) - anahtar(esk))
    eksik_toplam += eksik
    print(f"{ad:<9} {len(yeni):>5}   {len(esk):>12}   {eksik:>10}   {toplam(yeni):>8}"
          f"   {toplam(esk):>8}   {toplam(yeni) - toplam(esk):>5}")
print(f"tutarsiz (satir , yer) cifti: {eksik_toplam}")

# Kismi yenileme: ambar ve iki bolge pazari yenilendi , uc pazar yenilenmedi.
YENI = {"dogu", "bati"}
pazar_toplami = sum(toplam(pazar(TUM if b in YENI else ESKI, "bolge", b)) for b in BOLGE)
print(f"\nambarin 2026-07 toplami {toplam(TUM)} , bes bolge pazarinin toplami "
      f"{pazar_toplami} , fark {toplam(TUM) - pazar_toplami}")
```

```
ambar 2200 satir , alti pazar 3322 satir , saklanan toplam 5522
iki yerde duran satir 1078 , uc yerde duran 1122 , satir basina ortalama 2.51 kopya

pazar     satir   yenilenmemis  eksik satir   07 ambar   07 pazar   fark
dogu        374            356           18        958        665     293
bati        341            333            8        812        674     138
kuzey       506            492           14       1251       1041     210
guney       440            418           22       1039        780     259
merkez      539            521           18       1280       1116     164
isyeri     1122           1076           46       2738       2113     625
tutarsiz (satir , yer) cifti: 126

ambarin 2026-07 toplami 5340 , bes bolge pazarinin toplami 4707 , fark 633
```

Birinci sayı saklama tarafındadır: 2.200 satırlık bir veri kümesi altı pazarla birlikte 5.522 satır
olarak duruyor, satır başına 2,51 kopya. Beş bölge pazarı satırları ayrıştırdığı için her satırı
tam bir kez çoğaltıyor; tarife pazarı bunun üstüne biniyor ve 1.122 satırı **üçüncü** bir yere
yazıyor. Kopya sayısı düzenlerin sayısıyla değil, düzenlerin **kesişmesiyle** artar; kesişmeyen beş
pazar ikinci kopyayı, kesişen bir pazar üçüncüyü getirdi.

İkinci sayı tutarlılık tarafındadır ve saklama sayısından daha ağırdır. Geç gelen kayıt ambara
işlendiğinde ambar 2.200 satıra çıkıyor, tazelenmemiş pazarlar yazıldıkları andaki satırlarda
kalıyor. Ayrışma 126 (satır, yer) çiftindedir: seksen satır beş bölge pazarında, kırk altısı ayrıca
tarife pazarında eksik. Ayrışmanın sunulan sayıya yansıması sütunlardadır — doğu pazarı o dönem
için 665 diyor, ambar 958 diyor. Aynı soruya iki adres iki yanıt veriyor ve **hiçbir yerde hata
görünmüyor**; her iki taraf da kendi içinde tutarlı, eksiksiz ve tür bakımından kusursuz.

Üçüncü sayı çoğalmanın en sinsi biçimidir. Tazeleme hepsi için aynı anda yapılmaz; ambar ve iki
bölge pazarı yenilenmiş, üçü yenilenmemiş bir anda beş bölge pazarının toplamı 4.707, ambarın aynı
dönem toplamı 5.340 çıkıyor. Fark 633 m3 ve iki sayı da bir kaynağın kendi içinde doğru okumasıdır.
Ambar bölge pazarlarının **birleşimi** olduğu hâlde birleşimin toplamı ambarınkini vermiyor; veren
şey tazelemenin sırası. Bir alt küme çıkarıldığında ölçülmesi gereken üçüncü sayı, iki adresin ne
kadar süre farklı yanıt verdiğidir.

#### Özet

- Veri pazarı, ambardaki satırların tek bir alan değeri üzerinden çıkarılmış ve ayrı yazılmış alt
  kümesidir; özet değil, aynı satırların ikinci bir kopyasıdır.
- Üç soruluk kümede bölümlemesiz taban 13.560, en iyi merkezî düzen 3.256, iki pazar 2.580 öder;
  ama pazarın bölge sorularındaki kazancı **sıfırdır**, çünkü orada bölüm anahtarına bir alan
  eklemekle aynı budama elde edilir.
- Pazarın gerçek kazancı her alt kümenin **kendi bölüm anahtarını** seçebilmesidir: tarife
  sorusunda merkezî düzen 1.000, tarife pazarı 324 öder; üçüncü alanı merkezî anahtara eklemek
  toplamı 3.256'dan 4.500'e çıkarır.
- Bedel çoğalmadır: altı pazar 3.322 satır daha saklar, satır başına 2,51 kopya, 1.122 satır üç
  ayrı yerde durur. Kopya sayısını düzenlerin sayısı değil kesişmesi belirler.
- Tazeleme ayrıştığında 126 (satır, yer) çifti ayrışır ve aynı soru iki adreste iki yanıt verir
  (665 ve 958) — hiçbir hata görünmeden.
- Yarı yenilenmiş bir kümede beş bölge pazarının toplamı 4.707, ambarın aynı dönem toplamı 5.340
  olur; iki sayı da kendi kaynağında doğrudur.

#### Sonraki Adım

Bu derste alt kümeyi kimin çıkardığı hiç sorulmadı. Altı pazarın bölüm anahtarını, tazeleme
sırasını ve türetme kuralını tek bir taraf belirledi; bölge sorumlusu yalnızca kendisi için
yazılmış alt kümeyi okudu. Karar o tarafta kalmasaydı ne olurdu — düzen kararını bölgenin kendisi
verseydi, tazeleme takvimini kendisi seçseydi. İki sayı bu soruyla değişir: **düzen kararı alan
taraf sayısı** ve uçtan uca bir sorgunun **dokunduğu düzen sayısı**. Sonraki ders sahipliğin
dağıtıldığı iki modeli bu iki sayıyla ölçer ve aynı yerde bu kursun her maliyetinin dayandığı dosya
sabitini süpürerek hangi sonuçların o sabite bağlı olduğunu gösterir.

### Veri Örgüsü ve Veri Ağı

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/veri-orgusu-ve-veri-agi

Sahiplik dağıtıldığında değişen iki sayının ölçülmesi: düzen kararı alan taraf sayısı ve uçtan uca bir sorgunun dokunduğu düzen sayısı. Merkezî kurulum 1/1, veri örgüsü 1/5, veri ağı 5/5 veriyor; beş yerel soru artı bir ülke sorusundan oluşan karışımda maliyet 3.120, 2.000 ve 6.280 çıkıyor. Ağın pahalı olmasının nedeni dağıtım değil ölçülmemiş anahtar seçimidir: iki birimin kararı tek başına 2.450 tutuyor. Ders dosya sabitini 60, 120, 240 ve 480 değerlerinde süpürüyor: iki sayı sabitten bağımsız, sıralama değil — örgü ile merkezînin eşitlendiği yer 400 ve budanmayan tek bir soru eklendiğinde dağıtık kurulum dört değerde de kaybediyor.

Önceki derste altı alt küme çıkarıldı ve her birinin bölüm anahtarını, tazeleme sırasını ve
türetme kuralını **tek bir taraf** belirledi. Bölge birimi kendisi için yazılmış kümeyi yalnızca
okudu. Karar orada kalmasaydı ne olurdu: düzen anahtarını bölgenin kendisi seçseydi, yazma
takvimini kendisi kursaydı, kendi kümesini bir ürün gibi yayımlasaydı.

Bu soru bir mimari tercih gibi görünür, oysa iki sayıyı değiştirir ve o iki sayı ölçülebilir.
Birincisi **düzen kararı alan taraf sayısıdır**: kaç ayrı yerde "bu satırlar hangi anahtara göre
dosyalanacak" sorusu yanıtlanıyor. İkincisi **uçtan uca bir sorgunun dokunduğu düzen sayısıdır**:
bütün bölgeleri kapsayan tek bir soru kaç ayrı bölümleme kararının üstünden geçiyor. Ders bu iki
sayıyı üç düzenlemede ölçer, ardından bu kursun her maliyetinin dayandığı dosya sabitini süpürerek
hangi sonucun sabite bağlı olduğunu gösterir.

- **DY51.** Kaynak, hat ve depolama düzeni **kurgudur** ve ders içinde tanımlanır. Hiçbir ambar,
  göl, katalog ürünü, sorgu motoru ya da bulut depolama servisi çağrılmaz. Tohum 20260218.
- **DY52.** **Veri örgüsü (data fabric)**, satırların birden çok yerde yazıldığı ama **anahtar
  kararının tek bir standartta** kaldığı düzenlemedir. **Veri ağı (data mesh)**, satırları üreten
  birimin hem yazmaya hem **anahtar seçimine** sahip olduğu düzenlemedir; her birim kendi **veri
  ürününü** yayımlar.
- **DY53.** Sorgunun bildiği **adres sayısı** üçüncü bir sayı olarak ayrıca yazılır. Örgüde tek bir
  erişim noktası vardır, ağda beş. Bu sayı maliyeti değiştirmez.
- **DY54.** Soru karışımı ikidir. **A karışımı** beş yerel soru (her birim kendi bölgesinin bir
  dönemi) artı bir ülke sorusu (aynı dönem, bütün bölgeler). **B karışımı** A'nın üstüne
  **budanmayan** bir soru ekler: tek bir abonenin bütün dönemleri.
- **DY55.** Sahiplik ve yönetişim yordamı Siber Güvenlik müfredatındaki Yönetişim, Risk ve
  Uyumluluk kursunda, sahiplik boşluğunun sayısı ise Veri Mühendisliğine Giriş kursundaki Veri
  Yönetişimi dersinde ölçüldü; **burada tekrarlanmaz**. Bu ders sahipliği yalnız **hangi düzenin
  kimin elinde olduğu** olarak sayar.
- **DY56.** Dosya sabiti **bir seçimdir** ve bu derste süpürülür: 60, 120, 240, 480. Maliyet bir
  oran değil tam sayıdır; dosya oranı basılacaksa çözünürlük adımı 0,0091'dir.

#### Kararın Kaç Elde Olduğu

Üç düzenleme aynı 2.200 satırın üstüne kurulur. Merkezîde tek bir depo vardır ve anahtarı
`donem`'dir. Örgüde satırlar beş ayrı yere yazılır, fakat anahtarın ne olacağına tek bir taraf karar
verir ve beşi de `donem` kullanır. Ağda her birim kendi anahtarını seçer: iki birim `donem`, biri
`donem`+`tip`, biri hiç bölümleme yapmaz, biri yalnız `tip`'i seçer. Bu seçimler kötü niyetli
değildir; hiçbiri hangi soru için yapıldığını yazmamıştır.

```python
# KURGUDUR. Olcum aginin sayac okumalari, hat ve depolama duzeni ders icinde
# tanimlanir; hicbir ambar, gol, katalog urunu ya da sorgu motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


SUTUN = ("abone", "donem", "bolge", "tip", "endeks", "birim", "m3")
DOSYA_SABIT = 120          # bir dosyaya dokunmanin sabit bedeli , hucre cinsinden


def hattan(kaynak):
    v, _ = a1_ayikla(kaynak)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def yaz(kayitlar, bolumleme=()):
    duzen = {}
    for k in kayitlar:
        duzen.setdefault(tuple(k[a] for a in bolumleme), []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None, sutunlu=True):
    """Bolumleme anahtarindaki alan dosya budar , obur alan dosyanin tamamini okutur."""
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    kalan = {a: v for a, v in suzgec.items() if a not in bolumleme}
    dosya, hucre, satir = 0, 0, 0
    okunan_sutun = len(secilen) if sutunlu else len(SUTUN)
    for anahtar, kayit in duzen.items():
        if budayan:
            es = dict(zip(bolumleme, anahtar))
            if any(es[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * okunan_sutun
        satir += sum(1 for k in kayit if all(k[a] == v for a, v in kalan.items()))
    return {"dosya": dosya, "hucre": hucre, "eslesen_satir": satir,
            "maliyet": dosya * DOSYA_SABIT + hucre}


TUM = hattan(kaynak_uret())
SEC = ("abone", "m3")
D07 = {"donem": "2026-07"}
AB = {"abone": "A007"}
# Uc duzenleme. Anahtar secimi kimin elinde: merkezde , tek standartta , birimde.
MERKEZI = {"": ("donem",)}
ORGU = {b: ("donem",) for b in BOLGE}            # dagitik yazim , tek anahtar karari
AG = {"dogu": ("donem",), "bati": ("donem", "tip"), "kuzey": (),
      "guney": ("donem",), "merkez": ("tip",)}   # her birim kendi anahtarini secti
SAYIM = {"merkezi": (1, 1, 1), "orgu": (1, 5, 1), "ag": (5, 5, 5)}


def kur(anahtarlar):
    if list(anahtarlar) == [""]:
        return {"": yaz(TUM, anahtarlar[""])}
    return {b: yaz([k for k in TUM if k["bolge"] == b], anahtarlar[b]) for b in anahtarlar}


def olc(anahtarlar, duzenler):
    """Bes yerel soru (her birim kendi bolgesinin donemi) , bir ulke sorusu ,
    bir abone sorusu. Merkezi kurulumda ucu de tek duzene gider."""
    if list(anahtarlar) == [""]:
        d, a = duzenler[""], anahtarlar[""]
        yerel = sum(sorgu(d, a, SEC, dict(D07, bolge=b))["maliyet"] for b in BOLGE)
        return yerel, sorgu(d, a, SEC, D07)["maliyet"], sorgu(d, a, SEC, AB)["maliyet"]
    yerel = sum(sorgu(duzenler[b], anahtarlar[b], SEC, D07)["maliyet"] for b in BOLGE)
    abone = sum(sorgu(duzenler[b], anahtarlar[b], SEC, AB)["maliyet"] for b in BOLGE)
    return yerel, yerel, abone


KURULUM = [("merkezi", MERKEZI), ("orgu", ORGU), ("ag", AG)]
print(f"satir {len(TUM)} , dosya sabiti {DOSYA_SABIT} hucre\n")
print("duzenleme  duzen karari  dokunulan duzen  adres  dosya   yerel x5   ulke   abone")
for ad, anah in KURULUM:
    dz = kur(anah)
    y, u, a = olc(anah, dz)
    t, dd, ad_r = SAYIM[ad]
    print(f"{ad:<10} {t:>12}  {dd:>15}  {ad_r:>5}  {sum(len(x) for x in dz.values()):>5}"
          f"   {y:>8}  {u:>5}  {a:>6}")

print("\nagda birim basina yerel soru:  anahtar / dosya / maliyet")
dz = kur(AG)
for b in BOLGE:
    r = sorgu(dz[b], AG[b], SEC, D07)
    print(f"  {b:<7} {str(AG[b]):<18} {len(dz[b]):>3} dosyanin {r['dosya']:>2} tanesi"
          f" , maliyet {r['maliyet']:>5}")
```

```
satir 2200 , dosya sabiti 120 hucre

duzenleme  duzen karari  dokunulan duzen  adres  dosya   yerel x5   ulke   abone
merkezi               1                1      1     11       2600    520    5720
orgu                  1                5      1     55       1000   1000   11000
ag                    5                5      5     47       3140   3140   10040

agda birim basina yerel soru:  anahtar / dosya / maliyet
  dogu    ('donem',)          11 dosyanin  1 tanesi , maliyet   188
  bati    ('donem', 'tip')    22 dosyanin  2 tanesi , maliyet   302
  kuzey   ()                   1 dosyanin  1 tanesi , maliyet  1132
  guney   ('donem',)          11 dosyanin  1 tanesi , maliyet   200
  merkez  ('tip',)             2 dosyanin  2 tanesi , maliyet  1318
```

İlk üç sütun dersin iki sayısını taşır. Merkezîde karar bir elde ve uçtan uca sorgu tek düzene
dokunuyor: 1 ve 1. Örgüde satırlar beş yere dağılmış ama anahtar kararı hâlâ tek elde: 1 ve 5.
Ağda ikisi de beş. Adres sütunu üçüncü ayrımı gösterir; örgü beş depoyu tek bir erişim noktasının
arkasına koyar, ağ koymaz. **Adres sayısının maliyet sütunlarına hiçbir katkısı yoktur**: örgü
1.000 ödüyor, çünkü satırlar orada; adresin tek olması okunan hücreyi değiştirmiyor. Bir erişim
katmanı maliyeti değil, **kimin bilmek zorunda olduğunu** azaltır.

Maliyet sütunlarında iki şey birden okunur. Yerel sorularda örgü merkezîden üç kat ucuzdur (1.000'e
karşı 2.600), çünkü merkezî düzende bir bölgenin dönemini sormak o dönemin **bütün** satırlarını
okutur ve beş bölge aynı dosyayı beş kez okur. Dağıtık yazımda beş yerel soru ayrık kümelere iner
ve toplamları tam olarak ülke sorusunun maliyetine eşit çıkar. Buna karşılık ülke sorusunda merkezî
520, örgü 1.000 öder: aynı 400 hücre okunuyor, fark dosya sabitinin bir kez yerine beş kez
ödenmesi. Abone sorusunda uçurum açılıyor, 5.720'ye karşı 11.000, çünkü orada budayan hiçbir anahtar
yok ve elli beş dosyanın tamamına dokunuluyor.

Ağın 3.140'ı ise dağıtımın değil, **ölçülmemiş anahtar seçiminin** sayısıdır. Alt tablo bunu birim
birim ayırır: doğu 188, güney 200, batı 302 öderken kuzey 1.132 ve merkez 1.318 ödüyor. İki birimin
kararı tek başına 2.450 tutuyor, yani ağın yerel toplamının üçte ikisinden fazlası. Kuzey hiç
bölümleme yapmadığı için tek dosyasının tamamını okuyor; merkez `tip`'i seçtiği için dönem süzgeci
hiçbir şey budamıyor. Kursun kuralı burada ikinci kez görünüyor: **hangi sorgu için seçildiği
yazılmayan düzen ölçülmemiş sayılır** ve ölçülmemiş beş karar ölçülmüş tek karardan pahalıya mal
oluyor. Sahipliği dağıtmak bu farkı yaratmıyor; sahipliği ölçüsüz dağıtmak yaratıyor.

#### Dosya Sabiti Süpürülünce

Bu kursun bütün maliyetleri bir seçime dayanıyor: bir dosyaya dokunmanın bedeli 120 hücre. Sayı
kurgudur ve başka türlü olabilirdi. Yukarıdaki bütün karşılaştırmalar o seçime ne kadar borçlu,
bunun tek yolu sabiti değiştirip aynı ölçümü tekrarlamaktır.

```python
# Onceki blogun tanimlari surer: kur, olc, KURULUM, SAYIM, TUM, DOSYA_SABIT.
DUZENLEMELER = {ad: (anah, kur(anah)) for ad, anah in KURULUM}
AD = ("merkezi", "orgu", "ag")


def karisim():
    """A: bes yerel soru + bir ulke sorusu. B: A , ustune bir abone sorusu."""
    c = {}
    for ad, (anah, dz) in DUZENLEMELER.items():
        y, u, a = olc(anah, dz)
        c[ad] = (y + u, y + u + a)
    return c


print("sabit    merkezi-A    orgu-A      ag-A    merkezi-B    orgu-B      ag-B")
for sabit in (60, 120, 240, 480):
    DOSYA_SABIT = sabit
    c = karisim()
    print(f"{sabit:>5}  " + "  ".join(f"{c[a][0]:>9}" for a in AD)
          + "  " + "  ".join(f"{c[a][1]:>9}" for a in AD))

# Maliyet sabitte dogrusaldir: maliyet(sabit) = hucre + sabit x dokunulan dosya.
DOSYA_SABIT = 0
hucre = karisim()
DOSYA_SABIT = 1
egim = {a: tuple(karisim()[a][i] - hucre[a][i] for i in (0, 1)) for a in AD}
DOSYA_SABIT = 120
print("\nkurulum        A karisimi              B karisimi")
for a in AD:
    print(f"{a:<9}  {hucre[a][0]:>7} + sabit x {egim[a][0]:>3}"
          f"      {hucre[a][1]:>7} + sabit x {egim[a][1]:>3}")
esik = (hucre["merkezi"][0] - hucre["orgu"][0]) // (egim["orgu"][0] - egim["merkezi"][0])
print(f"\nA karisiminda orgu ile merkezinin esitlendigi sabit: {esik}")
print("sabitten bagimsiz olan: duzen karari alan taraf / dokunulan duzen , "
      + " , ".join(f"{a} {SAYIM[a][0]}/{SAYIM[a][1]}" for a in AD))
```

```
sabit    merkezi-A    orgu-A      ag-A    merkezi-B    orgu-B      ag-B
   60       2760       1400       5440       7820       9100      12660
  120       3120       2000       6280       8840      13000      16320
  240       3840       3200       7960      10880      20800      23640
  480       5280       5600      11320      14960      36400      38280

kurulum        A karisimi              B karisimi
merkezi       2400 + sabit x   6         6800 + sabit x  17
orgu           800 + sabit x  10         5200 + sabit x  65
ag            4600 + sabit x  14         9000 + sabit x  61

A karisiminda orgu ile merkezinin esitlendigi sabit: 400
sabitten bagimsiz olan: duzen karari alan taraf / dokunulan duzen , merkezi 1/1 , orgu 1/5 , ag 5/5
```

Süpürme üç ayrı türde sonuç veriyor. Birincisi **sabitten bağımsız** olanlardır: düzen kararı alan
taraf ve dokunulan düzen sayıları dört değerde de 1/1, 1/5 ve 5/5 kalıyor. Bu dersin iki ana sayısı
bir maliyet modelinin değil, bir örgütlenme kararının sayısıdır ve dosya sabiti onlara dokunmaz.
Aynı biçimde ağın örgüden pahalı olması da dört değerde sürüyor; ölçülmemiş anahtar seçiminin
bedeli sabitten bağımsızdır.

İkincisi **sabite bağlı olan sıralamadır** ve bu dersin en önemli uyarısıdır. A karışımında örgü
60, 120 ve 240'ta merkezîden ucuzdur, 480'de değildir. Doğrusal biçim nedenini veriyor: örgü 800
hücreye karşılık her dosya için 10, merkezî 2.400 hücreye karşılık 6 ödüyor. Örgünün hücre
avantajı sabit, dosya dezavantajı sabitle çarpılıyor; ikisi tam olarak 400'de eşitleniyor.
Sabitin 120 seçilmiş olması "dağıtık yazım yerel soruları ucuzlatır" cümlesini doğru yapıyor;
480 seçilseydi aynı cümle yanlış olurdu. **Bir düzen karşılaştırması, dayandığı sabit yazılmadan
aktarılamaz.**

Üçüncüsü **soru karışımına bağlı olan** sonuçtur ve sabitin süpürülmesi onu ortadan kaldırmıyor.
B karışımı A'nın üstüne budanmayan tek bir soru ekliyor ve sıralama dört değerde de tersine
dönüyor: merkezî 7.820, örgü 9.100 ile başlıyor, 480'de fark 14.960'a karşı 36.400 oluyor. Eğim
sütunu bunu açıklıyor: B'de örgünün dosya katsayısı 65, merkezînin 17. Budayan bir anahtarın
olmadığı tek bir soru, dağıtık yazımın dosya sayısını doğrudan maliyete çeviriyor. Dağıtmak da
bozar; bozduğu yer, hiçbir anahtarın yardım etmediği sorulardır.

#### Özet

- Sahipliğin dağıtılması iki sayıyı değiştirir: düzen kararı alan taraf sayısı ve uçtan uca bir
  sorgunun dokunduğu düzen sayısı. Merkezî 1/1, veri örgüsü 1/5, veri ağı 5/5 verir.
- Örgü ile ağın ayrımı yazmanın dağıtılması değil, **anahtar kararının** dağıtılmasıdır; sorgunun
  bildiği adres sayısı üçüncü bir sayıdır ve maliyeti hiç değiştirmez.
- Ağın yerel toplamı 3.140, örgününki 1.000; farkın kaynağı dağıtım değil ölçülmemiş anahtar
  seçimidir, iki birimin kararı tek başına 2.450 tutar.
- Dosya sabiti süpürüldüğünde iki ana sayı ve ağın örgüden pahalı olması dört değerde de değişmez;
  değişen şey örgü ile merkezînin sıralamasıdır, eşitlik noktası 400'dür.
- Maliyet sabitte doğrusaldır (`hücre + sabit x dosya`), bu yüzden bir karşılaştırma dayandığı
  sabit yazılmadan aktarılamaz.
- Budanmayan tek bir soru eklendiğinde dağıtık kurulum dört sabit değerinde de kaybeder: B
  karışımında merkezî 8.840, örgü 13.000, ağ 16.320 öder.

#### Sonraki Adım

Bu derste beş depo, kırk yedi dosya ve beş ayrı anahtar kararı sayıldı. Ölçülen her maliyet, sorunun
**hangi depoya gideceğinin zaten bilindiği** varsayımına dayanıyordu. Ağ düzenlemesinde beş adres
vardı ve sorgu hepsini biliyordu; oysa bir bölge biriminin ürününün adı, taşıdığı sütunlar,
bölüm anahtarı ve hangi dönemleri kapsadığı bir yerde yazılı olmadıkça bulunmaz. Sonraki ders
bunu tek bir sayıya çevirir: bir veri varlığını bulmak için kaç girişe bakılıyor, katalog varken ve
yokken. Aynı derste kataloğun kendi kusuru ölçülür — kaç giriş artık var olmayan bir düzeni
gösteriyor — ve altı dersin ardından bu konu kapanır.

### Meta Veri Kataloğu

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/meta-veri-katalogu

Bir veri varlığını bulmak için bakılan giriş sayısının katalog varken ve yokken sayılması: on iki varlık ve 119 dosyalık bir kümede beş arama katalogsuz 295, katalogla 29 giriş bakıyor ve en pahalı arama 117'den 11'e iniyor. Katalog bayatladığında sayı geri döner: üç varlık değişince on iki girişin üçü artık var olmayan bir düzeni gösteriyor, aynı beş arama katalogsuz 406, bayat katalogla 251 oluyor, fakat iki arama katalogsuz olandan pahalıya bitiyor (129'a karşı 122 ve 104'e karşı 100), çünkü ölü giriş taranıyor ve ardından tam tarama yine yapılıyor.

Önceki iki ders bir küme varlık biriktirdi: bir merkezî ambar, birim odaklı alt kümeler ve her biri
kendi bölüm anahtarını seçmiş birim ürünleri. Her ölçümde sorunun **hangi depoya gideceği**
zaten biliniyordu; maliyet, adres bilindikten sonra başlıyordu. Gerçekte o adres bir yerde yazılı
değilse bulunmaz. Bir birimin ürününün adı, sahibi, taşıdığı sütunlar, bölüm anahtarı ve hangi
dönemleri kapsadığı yazılmamışsa, bir soruyu yanıtlayacak varlığı bulmanın kendisi bir arama işidir.

Bu dersin ölçtüğü şey o aramadır. **Meta veri kataloğu (metadata catalog)** varlık başına tek bir
giriş tutar ve arama o girişler üzerinde yapılır; katalog yoksa arama varlıkların dosyaları
üzerinde yapılır. İki durumda **bakılan giriş sayısı** sayılır. Ardından kataloğun kendi kusuru
ölçülür: depolama tarafı değiştiğinde kaç giriş artık var olmayan bir düzeni gösteriyor ve bu
girişlere güvenen arama ne kadara mal oluyor.

- **DY61.** Kaynak, hat, depolama düzeni ve katalog **kurgudur** ve ders içinde tanımlanır. Hiçbir
  ambar, göl, katalog ürünü ya da sorgu motoru çağrılmaz. Tohum 20260218.
- **DY62.** **Bakılan giriş** iki türlüdür ve ikisi de tek tek sayılır. Katalogsuz aramada bir
  varlığın neyi kapsadığını öğrenmek için **dosyalarının açılması** gerekir; bakılan giriş
  **açılan dosyadır**. Katalogla aramada bakılan giriş **taranan katalog girişidir**.
- **DY63.** Varlıklar **ada göre sıralı** taranır. Arama, göreve yeten **ilk** varlıkta durur;
  en ucuz varlığı seçme kararı bu dersin ölçüsünün dışındadır.
- **DY64.** Katalog girişi **yazıldığı andaki** düzeni taşır. Depolama tarafındaki bir değişiklik
  girişe kendiliğinden yansımaz; yansıması ayrı bir iştir.
- **DY65.** Katalogla yapılan arama girişe güvenir ama **doğrular**: eşleşen girişin varlığı
  gerçekten göreve yetmiyorsa arama katalogsuz aramaya düşer ve iki bedel **toplanır**.
- **DY66.** Soyağacı ölçüsü Veri Mühendisliğine Giriş kursundaki Veri Soyağacı dersinde ölçüldü;
  **burada tekrarlanmaz**. Soyağacının nesnesi bir **satırın dayandığı satırlardır**; kataloğun
  nesnesi bir **varlığın nerede durduğudur**. Biri değeri açıklar, öbürü adresi verir.

#### Katalog Varken ve Yokken

Küme on iki varlıktan oluşur. Merkezî ambar bütün bölgeleri ve tarife sütununu taşır ama saklama
penceresi yüzünden yalnız son altı dönemi tutar. Beş bölge pazarı bütün dönemleri taşır, tarife
sütununu taşımaz. Bir tarife pazarı işyeri satırlarını bütün bölgelerden toplar. Beş birim ürünü
kendi bölgesinin bütün satırlarını, kendi seçtiği anahtarla saklar. Beş arama görevi bu küme
üzerinde koşar.

```python
# KURGUDUR. Olcum aginin sayac okumalari, hat ve depolama duzeni ders icinde
# tanimlanir; hicbir ambar, gol, katalog urunu ya da sorgu motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        cikti.append(dict(k, m3=int(k["endeks"]) - int(onceki["endeks"])))
    return cikti, dusen


def hattan(kaynak):
    v, _ = a1_ayikla(kaynak)
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def yaz(kayitlar, bolumleme=()):
    duzen = {}
    for k in kayitlar:
        duzen.setdefault(tuple(k[a] for a in bolumleme), []).append(k)
    return duzen


TUM = hattan(kaynak_uret())
SON6 = sorted({k["donem"] for k in TUM})[-6:]
TAM = ("abone", "donem", "bolge", "tip", "m3")
BOLGESEL = ("abone", "donem", "bolge", "m3")     # tarife sutunu tasinmiyor
AGIN_ANAHTARI = {"dogu": ("donem",), "bati": ("donem", "tip"), "kuzey": (),
                 "guney": ("donem",), "merkez": ("tip",)}


def varlik(ad, sahip, kayitlar, anahtar, sutun):
    """Bir veri varligi: satirlari , bolumleme anahtari ve tasidigi sutunlar."""
    duzen = yaz(kayitlar, anahtar)
    return {"ad": ad, "sahip": sahip, "anahtar": anahtar, "sutun": sutun,
            "dosya": len(duzen), "satir": {(k["abone"], k["donem"]) for k in kayitlar},
            "bolge": {k["bolge"] for k in kayitlar}, "tip": {k["tip"] for k in kayitlar},
            "donem": {k["donem"] for k in kayitlar}}


def kurulum():
    """Onceki iki dersin biriktirdigi varliklar , ada gore siralanmis."""
    v = [varlik("ambar_merkezi", "merkez ekibi",
                [k for k in TUM if k["donem"] in SON6], ("donem",), TAM)]
    for b in BOLGE:
        v.append(varlik(f"pazar_{b}", f"{b} raporlama",
                        [k for k in TUM if k["bolge"] == b], ("donem",), BOLGESEL))
    v.append(varlik("pazar_tarife", "tarife ekibi",
                    [k for k in TUM if k["tip"] == "isyeri"], ("donem",), TAM))
    for b in BOLGE:
        v.append(varlik(f"urun_{b}", f"{b} birimi",
                        [k for k in TUM if k["bolge"] == b], AGIN_ANAHTARI[b], TAM))
    return sorted(v, key=lambda x: x["ad"])


def gereken(gorev):
    return {(k["abone"], k["donem"]) for k in TUM
            if all(k[a] == d for a, d in gorev.items())}


def gercekte_kapsiyor(v, gorev):
    """Varligin kendisine bakarak: istenen satirlarin hepsi burada mi."""
    return set(gorev) <= set(v["sutun"]) and gereken(gorev) <= v["satir"]


def kayda_gore_kapsiyor(g, gorev):
    """Yalnizca katalog girisine bakarak , dosya acmadan."""
    if not set(gorev) <= set(g["sutun"]):
        return False
    if gorev["donem"] not in g["donem"] or gorev["bolge"] not in g["bolge"]:
        return False
    return {gorev["tip"]} <= g["tip"] if "tip" in gorev else g["tip"] == set(TIP)


def katalog_yaz(varliklar):
    """Varlik basina tek giris: ad , sahip , anahtar , dosya , sutun , kapsam."""
    return [{a: v[a] for a in ("ad", "sahip", "anahtar", "dosya", "sutun",
                               "bolge", "tip", "donem")} for v in varliklar]


def katalogsuz_ara(varliklar, gorev):
    """Sirayla her varligin dosyalari acilir; bakilan giris = acilan dosya."""
    bakilan = 0
    for v in varliklar:
        bakilan += v["dosya"]
        if gercekte_kapsiyor(v, gorev):
            return bakilan, v["ad"]
    return bakilan, None


def kataloglu_ara(varliklar, katalog, gorev):
    """Girisler taranir; eslesen girisin varligi dogrulanir. Bulunamazsa
    katalogsuz aramaya dusulur ve iki maliyet toplanir."""
    ada_gore = {v["ad"]: v for v in varliklar}
    for i, g in enumerate(katalog, 1):
        if not kayda_gore_kapsiyor(g, gorev):
            continue
        v = ada_gore.get(g["ad"])
        if v is not None and gercekte_kapsiyor(v, gorev):
            return i, g["ad"], False
        bakilan, ad = katalogsuz_ara(varliklar, gorev)
        return i + bakilan, ad, True
    return len(katalog), None, False


GOREV = [("kuzey / 2026-03 / isyeri", {"bolge": "kuzey", "donem": "2026-03", "tip": "isyeri"}),
         ("guney / 2026-02", {"bolge": "guney", "donem": "2026-02"}),
         ("bati / 2026-11 / mesken", {"bolge": "bati", "donem": "2026-11", "tip": "mesken"}),
         ("merkez / 2026-02", {"bolge": "merkez", "donem": "2026-02"}),
         ("kuzey / 2026-05 / mesken", {"bolge": "kuzey", "donem": "2026-05", "tip": "mesken"})]

VARLIK = kurulum()
KATALOG = katalog_yaz(VARLIK)
print(f"varlik {len(VARLIK)} , dosya {sum(v['dosya'] for v in VARLIK)} , "
      f"katalog girisi {len(KATALOG)}\n")
print("varlik            sahip             anahtar             dosya  donem  sutun")
for v in VARLIK:
    print(f"{v['ad']:<17} {v['sahip']:<17} {str(v['anahtar']):<19} {v['dosya']:>4}"
          f"   {len(v['donem']):>4}   {len(v['sutun']):>4}")

print("\ngorev                       katalogsuz  kataloglu   bulunan varlik")
t1 = t2 = 0
for ad, g in GOREV:
    b1, v1 = katalogsuz_ara(VARLIK, g)
    b2, v2, _ = kataloglu_ara(VARLIK, KATALOG, g)
    t1, t2 = t1 + b1, t2 + b2
    print(f"{ad:<27} {b1:>10}  {b2:>9}   {v1}")
print(f"{'toplam':<27} {t1:>10}  {t2:>9}")
```

```
varlik 12 , dosya 119 , katalog girisi 12

varlik            sahip             anahtar             dosya  donem  sutun
ambar_merkezi     merkez ekibi      ('donem',)             6      6      5
pazar_bati        bati raporlama    ('donem',)            11     11      4
pazar_dogu        dogu raporlama    ('donem',)            11     11      4
pazar_guney       guney raporlama   ('donem',)            11     11      4
pazar_kuzey       kuzey raporlama   ('donem',)            11     11      4
pazar_merkez      merkez raporlama  ('donem',)            11     11      4
pazar_tarife      tarife ekibi      ('donem',)            11     11      5
urun_bati         bati birimi       ('donem', 'tip')      22     11      5
urun_dogu         dogu birimi       ('donem',)            11     11      5
urun_guney        guney birimi      ('donem',)            11     11      5
urun_kuzey        kuzey birimi      ()                     1     11      5
urun_merkez       merkez birimi     ('tip',)               2     11      5

gorev                       katalogsuz  kataloglu   bulunan varlik
kuzey / 2026-03 / isyeri            72          7   pazar_tarife
guney / 2026-02                     39          4   pazar_guney
bati / 2026-11 / mesken              6          1   ambar_merkezi
merkez / 2026-02                    61          6   pazar_merkez
kuzey / 2026-05 / mesken           117         11   urun_kuzey
toplam                             295         29
```

Aradaki oran on kattır: beş arama katalogsuz 295, katalogla 29 giriş baktırıyor. Farkın kaynağı
ölçek değil **birimdir**. Katalogsuz arama varlık başına o varlığın **bütün dosyalarını** açar,
çünkü bir varlığın hangi dönemleri ya da hangi tarifeleri kapsadığını bilmenin başka yolu yoktur;
katalogla arama varlık başına **tek giriş** okur. On iki varlık 119 dosya tutuyor, katalog aynı
kümeyi 12 girişle anlatıyor. Katalog bir dizin değildir ve sorguyu hızlandırmaz; **varlık sayısı
ile dosya sayısı arasındaki oranı** aramaya kazandırır.

Görevler arasındaki dağılım da okunmalıdır. En ucuz arama batı için olandır: merkezî ambar hem
tarife sütununu taşır hem de o dönemi kapsar ve sıralamada ilk varlıktır. En pahalısı kuzeyin
`2026-05` dönemidir: ambar saklama penceresi yüzünden düşer, beş bölge pazarı tarife sütununu
taşımadığı için düşer, tarife pazarı yalnız işyerini tuttuğu için düşer ve arama on birinci
varlığa kadar sürer — katalogsuz 117 dosya, katalogla 11 giriş. **Bir varlığı bulmanın bedelini
kümenin büyüklüğü değil, aranan şeyin sıradaki yeri belirler**; katalogun yaptığı o sırayı dosya
biriminden giriş birimine çevirmektir.

#### Katalogun Bayatlaması

Katalog yazıldığı andaki düzeni taşır. Depolama tarafı durmaz: bir pazar kaldırılır, bir birim
ürününü yeniden bölümler, bir pazara saklama penceresi uygulanır. Bu üç değişiklik girişlere
kendiliğinden yansımaz. Aşağıdaki koşum önce kaç girişin yanlışlandığını sayar, sonra aynı beş
aramayı **eski katalogla yeni küme** üzerinde tekrarlar.

```python
# Onceki blogun tanimlari surer: VARLIK, KATALOG, GOREV, varlik, TUM, SON6, TAM,
# BOLGESEL, katalogsuz_ara, kataloglu_ara.
def duzen_degisti(varliklar):
    """Katalog yazildiktan sonra depolama tarafinda olan uc degisiklik."""
    yeni = []
    for v in varliklar:
        if v["ad"] == "pazar_tarife":                 # varlik kaldirildi
            continue
        if v["ad"] == "urun_kuzey":                   # yeniden bolumlendi
            yeni.append(varlik(v["ad"], v["sahip"],
                               [k for k in TUM if k["bolge"] == "kuzey"],
                               ("donem", "tip"), TAM))
        elif v["ad"] == "pazar_guney":                # saklama penceresi daraldi
            yeni.append(varlik(v["ad"], v["sahip"],
                               [k for k in TUM if k["bolge"] == "guney"
                                and k["donem"] in SON6], ("donem",), BOLGESEL))
        else:
            yeni.append(v)
    return yeni


YENI = duzen_degisti(VARLIK)
ada_gore = {v["ad"]: v for v in YENI}
print("bayat giris   kayitli anahtar / dosya / donem   gercek anahtar / dosya / donem")
bayat = 0
for g in KATALOG:
    v = ada_gore.get(g["ad"])
    if v is not None and all(g[a] == v[a] for a in ("anahtar", "dosya", "donem")):
        continue
    bayat += 1
    gercek = ("varlik yok" if v is None
              else f"{str(v['anahtar']):<18} {v['dosya']:>3}  {len(v['donem']):>3}")
    print(f"{g['ad']:<14} {str(g['anahtar']):<18} {g['dosya']:>3}  {len(g['donem']):>3}"
          f"   {gercek}")
print(f"\n{len(KATALOG)} girisin {bayat} tanesi artik var olmayan bir duzeni gosteriyor.")

print("\ngorev                       katalogsuz  kataloglu  geri dusme  bulunan varlik")
t1 = t2 = 0
for ad, gor in GOREV:
    b1, v1 = katalogsuz_ara(YENI, gor)
    b2, v2, dus = kataloglu_ara(YENI, KATALOG, gor)
    t1, t2 = t1 + b1, t2 + b2
    print(f"{ad:<27} {b1:>10}  {b2:>9}  {'evet' if dus else 'hayir':>10}   {v2}")
print(f"{'toplam':<27} {t1:>10}  {t2:>9}")
```

```
bayat giris   kayitli anahtar / dosya / donem   gercek anahtar / dosya / donem
pazar_guney    ('donem',)          11   11   ('donem',)           6    6
pazar_tarife   ('donem',)          11   11   varlik yok
urun_kuzey     ()                   1   11   ('donem', 'tip')    22   11

12 girisin 3 tanesi artik var olmayan bir duzeni gosteriyor.

gorev                       katalogsuz  kataloglu  geri dusme  bulunan varlik
kuzey / 2026-03 / isyeri           122        129        evet   urun_kuzey
guney / 2026-02                    100        104        evet   urun_guney
bati / 2026-11 / mesken              6          1       hayir   ambar_merkezi
merkez / 2026-02                    56          6       hayir   pazar_merkez
kuzey / 2026-05 / mesken           122         11       hayir   urun_kuzey
toplam                             406        251
```

Üç değişiklik on iki girişin üçünü yanlışladı ve üçü aynı türden değil. `urun_kuzey` girişi hâlâ
var olan bir varlığı gösteriyor; yanlış olan yalnız kayıtlı anahtar ile dosya sayısı, bir yerine
yirmi iki. Bu giriş aramayı bozmuyor, **maliyet kestirimini** bozuyor: o varlığa gideceğini bilen
bir taraf bir dosyaya dokunacağını sanıyor. `pazar_guney` girişi on bir dönem vaat ediyor, varlık
altı dönem taşıyor. `pazar_tarife` girişi hiç olmayan bir varlığı gösteriyor. **Bir katalog
girişinin yeşil olması o düzenin ölçüldüğü anlamına gelmez** ve yanlışlığın üç ayrı derecesi
vardır: eksik kestirim, eksik kapsam, hiç yokluk.

Son tablo bunun aramaya ne yaptığını gösteriyor. Katalog toplamda hâlâ kazandırıyor, 406'ya karşı
251. Ama iki satırda kazanç eksiye dönüyor. Kuzeyin `2026-03` araması yedi giriş tarıyor, ölü
girişe gidiyor, varlığı bulamıyor ve baştan tam taramaya düşüyor: 129, oysa katalog hiç olmasa
122. Güneyin `2026-02` araması dört giriş tarıyor, girişin vaat ettiği dönemi varlıkta bulamıyor
ve aynı yolu izliyor: 104, katalogsuz 100. Fark küçük ama işareti önemlidir; **bayat bir giriş
aramanın önüne bir yanlış adres koyar** ve o adres denenmeden elenemez. Kataloğun bedeli girişleri
yazmak değil, **girişleri düzenle aynı hızda tazelemektir**.

#### Özet

- Meta veri kataloğu varlık başına tek giriş tutar: ad, sahip, bölüm anahtarı, dosya sayısı,
  sütunlar ve kapsanan dönem ile bölge. Bir dizin değildir; sorguyu değil **aramayı** ucuzlatır.
- On iki varlık ve 119 dosyalık kümede beş arama katalogsuz 295, katalogla 29 giriş baktırır;
  kazanç varlık sayısı ile dosya sayısı arasındaki orandan gelir.
- Bir aramanın bedelini kümenin büyüklüğü değil aranan varlığın sıradaki yeri belirler: en ucuz
  arama 6'ya karşı 1, en pahalı arama 117'ye karşı 11 giriş bakar.
- Üç değişiklik on iki girişin üçünü yanlışlar ve üç ayrı derecede: eksik kestirim (dosya sayısı
  1 yerine 22), eksik kapsam (on bir dönem yerine altı) ve hiç yokluk.
- Bayat katalogla aynı beş arama 406'ya karşı 251 giriş baktırır, ama iki arama katalogsuz olandan
  pahalıya biter: 129'a karşı 122 ve 104'e karşı 100.
- Kataloğun bedeli girişleri yazmak değil, girişleri düzenle aynı hızda tazelemektir; tazelenmeyen
  giriş aramanın önüne yanlış bir adres koyar.

#### Sonraki Adım

Altı derste verinin **nerede durduğu** ayrıldı: şemaya uyan yer ile ham yer, ikisini bir arada
tutan düzen, alt küme çıkarmanın kazandırdığı ve çoğalttığı, sahipliğin dağıtılmasının iki sayısı
ve varlığı bulmanın giriş cinsinden bedeli. Bütün bu ölçümlerin ortak bir sessiz varsayımı vardı:
satır hep aynı biçimde duruyordu — abone, dönem, bölge, tarife ve tüketim yan yana, tek bir düz
sıra hâlinde. O biçim hiç sorgulanmadı. Oysa bir dosyaya yazılan şey bir satır listesi değil, bir
**tablo şeklidir**; hangi alanların ölçüm hangilerinin niteleme olduğu, aynı nitelemenin kaç kez
tekrarlandığı ve bir niteleme zaman içinde değiştiğinde eski değerinin saklanıp saklanmadığı ayrı
kararlardır. Sonraki konu bu kararları ölçer: aynı veri, aynı yer, farklı **şekil**.

## Modelleme ve Biçim

Tablonun şeklinin ve diske iniş biçiminin sorguya ödettiğinin sayılması: tek düz tabloya karşı olgu ile boyutun ayrılmasında saklanan hücre, birleştirme sayısı ve aynı sonuç, yıldız şemasında 2200 birleştirme ile 212 boyut satırına karşı kar tanesinde 4400 birleştirme ile 217 satır ve beş satır kazanmak için birleştirmenin iki katına çıkması, tarihçe tutmayan boyutun sorguyu yavaşlatmadan cevabı yanlışlaması ve işyeri toplamının 33145 yerine 35893 çıkarak 2748 metreküpü yanlış kovaya göndermesine karşılık tarihçe tutmanın boyut satırını 200'den 2200'e çıkarması, iki sütun seçildiğinde sütunlu okumanın 400 satırlı okumanın 1400 hücre okuması ve sütun sayısı arttıkça farkın kapandığı nokta, bölüm anahtarı dışındaki süzgeçte maliyetin 4520'den 11000'e çıkması ile budayan sorguda bile ince bölümlemenin okunan hücreyi hiç artırmadan maliyeti 520'den 1600'e taşıması, arşive taşımanın sıcak katmanda kazandırdığı ile geriye dönük yanıtlanamaz kıldığı soru.

### Boyutsal Modelleme

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/boyutsal-modelleme

Olgu ile boyutun ayrılmasının sakladığı hücreyi ve sorguya ödettiği birleştirmeyi ölçme: aynı 2200 satır tek düz tabloda 15400 hücre tutarken olgu artı iki boyut düzeninde 7236 hücre tutuyor, 8164 hücre eksiliyor ve iki düzenin ürettiği tip başına toplam birebir aynı kalıyor; karşılığında sorgu tek dosyaya dokunup maliyet 4520 öderken olgu artı boyut iki dosyaya dokunup 4800 hücre okuyor, maliyet 5040 oluyor ve sıfır birleştirme 2200'e çıkıyor; bir bölgenin adı düzeltildiğinde düz tabloda 506 satır, boyutta 46 satır güncelleniyor.

Altı derste verinin **nerede** durduğu ayrıldı: şema dayatan katman, ham katman, ikisini birleştiren
kurulum, alan odaklı alt kümeler, sahipliğin dağıtıldığı düzenler ve varlığı bulmayı sağlayan
katalog. Hepsinin ortak varsayımı vardı: satırlar bir yerde durur, biri onları arar. Hiçbir ölçüm
satırların **hangi biçimde** durduğunu sormadı.

Bu ders o soruyu sorar ve en eski karardan başlar: ölçülen değer ile ölçümü niteleyen bilgi aynı
tabloda mı durmalı. Sayaç okumasından çıkan tüketim bir sayıdır; abonenin bölgesi ve tipi, dönemin
yılı ve ayı o sayıyı niteler. Tek tablo ikisini yan yana taşır. Boyutsal modelleme onları ayırır.
Ayrımın saklanan hücreye, sorgunun maliyetine ve **çıkan sonuca** ne yaptığı sayılabilir bir şeydir.

- **AM1.** Kaynak, hat ve depo **kurgudur**; gerçek bir ambar, göl, dosya biçimi ya da sorgu motoru
  çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **AM2.** Hattan çıkan satır **2200**'dür: 200 abone ile on iki dönemin çarpımı 2400, ilk dönemin
  tüketimi hesaplanamadığı için 200 satır düşer.
- **AM3.** **Olgu tablosu (fact table)** ölçülen değeri taşır; **boyut bağıntısı (dimension table)** o
  değeri niteleyen bilgiyi taşır. Olgunun **taneciği (grain)** abone ile dönemin çiftidir.
- **AM4.** Saklanan hücre satır ile sütunun çarpımıdır. Bir alan bir hücredir; alanın genişliği
  ölçülmez.
- **AM5.** Maliyet `dosya x 120 + hücre` biçiminde okunur. **Dosya sabiti 120 bir seçimdir**; dosya
  sayısına duyarlı her sonuç sabitin değeriyle birlikte yazılır.
- **AM6.** **Birleştirme (join)** sayısı, sorgunun boyut bağıntısına kaç kez baktığıdır. Ham süre
  yazılmaz.
- **AM7.** Taban çizgisi **tek düz tablodur**: bölümlemesiz, tek dosya, birleştirme sayısı **0**.
- **AM8.** Çözünürlük: satır oranının adımı **0,0005**. **Toplamlar oran olarak değil, değerin
  kendisi olarak yazılır.**

#### Tek Tablonun Taşıdığı

Hattın beşinci aşamasından önceki satır yedi alan taşır: abone, dönem, bölge, tip, endeks, birim ve
hesaplanmış tüketim. Bu satırı olduğu gibi diske indirmek en az karar gerektiren düzendir; sorgu tek
bir yere bakar, hiçbir tabloyu bir başkasıyla eşlemek zorunda kalmaz. Karşılığında abonenin bölgesi
o abonenin her satırında yeniden yazılır. Bir abonenin on bir satırı varsa bölge adı on bir kez
saklanır.

İlk blok ortak tanımı kurar: kurgu kaynak, hattın satır üreten üç aşaması, düzen yazma ve maliyet
ölçen sorgu. Boyutsal ayrım `yildiz` işlevindedir: olguya yalnızca abone, dönem ve tüketim kalır,
niteleyen alanlar iki boyut bağıntısına çıkar.

```python
# KURGUDUR. Sayac okumalari, hat ve depo ders icinde tanimlanir; gercek bir
# ambar, gol, dosya bicimi ya da sorgu motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


SUTUN = ("abone", "donem", "bolge", "tip", "endeks", "birim", "m3")
DOSYA_SABIT = 120          # her dosyaya dokunmanin sabit bedeli , hucre cinsinden


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def yaz(kayitlar, bolumleme=()):
    duzen = {}
    for k in kayitlar:
        anahtar = tuple(k[a] for a in bolumleme)
        duzen.setdefault(anahtar, []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None, sutunlu=True):
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    kalan = {a: v for a, v in suzgec.items() if a not in bolumleme}
    dosya, hucre, satir = 0, 0, 0
    okunan_sutun = len(secilen) if sutunlu else len(SUTUN)
    for anahtar, kayit in duzen.items():
        if budayan:
            eslesme = dict(zip(bolumleme, anahtar))
            if any(eslesme[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * okunan_sutun
        satir += sum(1 for k in kayit if all(k[a] == v for a, v in kalan.items()))
    return {"dosya": dosya, "hucre": hucre, "eslesen_satir": satir,
            "maliyet": dosya * DOSYA_SABIT + hucre}


def yildiz(kayitlar):
    """Olgu tablosu artı iki boyut. Niteleyen alanlar olgudan cikarilir."""
    olgu = [{"abone": k["abone"], "donem": k["donem"], "m3": k["m3"]} for k in kayitlar]
    d_abone = {k["abone"]: {"bolge": k["bolge"], "tip": k["tip"]} for k in kayitlar}
    d_donem = {d: {"yil": d[:4], "ay": d[5:]} for d in DONEM}
    return olgu, {"abone": d_abone, "donem": d_donem}
```

#### Ayrımın Sakladığı Hücre

İkinci blok iki düzeni yan yana kurar ve saklanan hücreyi sayar. Ölçümün dürüst olması için bir ara
satır gerekir. `yildiz` işlevi olguyu kurarken endeks ile birim alanlarını hiç taşımaz, çünkü
tüketim hesaplandıktan sonra ikisine de gerek kalmaz. Bu, boyutsal ayrımın değil **alan düşürmenin**
kazancıdır ve ayrı sayılmazsa ayrıma yazılır. Tablo bu yüzden düz tabloyu iki kez listeler: yedi
alanla ve olgunun taşıdığı beş alanla. Ardından aynı soru iki düzene de sorulur: tip başına toplam
tüketim ne kadar.

```python
SATIR = satirlar()
OLGU, BOYUT = yildiz(SATIR)
TASINAN = ("abone", "donem", "bolge", "tip", "m3")
print(f"{'tablo':<22}{'satir':>7}{'sutun':>7}{'saklanan hucre':>16}")
for ad, n, s in (("duz tablo , yedi alan", len(SATIR), len(SUTUN)),
                 ("duz tablo , bes alan", len(SATIR), len(TASINAN)),
                 ("olgu", len(OLGU), 3),
                 ("boyut abone", len(BOYUT["abone"]), 3),
                 ("boyut donem", len(BOYUT["donem"]), 3)):
    print(f"{ad:<22}{n:>7}{s:>7}{n * s:>16}")
duz7 = len(SATIR) * len(SUTUN)
duz5 = len(SATIR) * len(TASINAN)
yil = len(OLGU) * 3 + len(BOYUT["abone"]) * 3 + len(BOYUT["donem"]) * 3
print(f"{'yildiz toplam':<22}{'':>7}{'':>7}{yil:>16}")
print("alan dusurmenin kazandirdigi:", duz7 - duz5)
print("ayrimin kazandirdigi:", duz5 - yil, " boyut satiri:",
      len(BOYUT["abone"]) + len(BOYUT["donem"]))


def duz_tip_toplam(kayitlar):
    top = {}
    for k in kayitlar:
        top[k["tip"]] = top.get(k["tip"], 0) + k["m3"]
    return top, 0


def yildiz_tip_toplam(olgu, boyut):
    top, birlestirme = {}, 0
    for o in olgu:
        t = boyut["abone"][o["abone"]]["tip"]
        birlestirme += 1
        top[t] = top.get(t, 0) + o["m3"]
    return top, birlestirme


t_duz, b_duz = duz_tip_toplam(SATIR)
t_yil, b_yil = yildiz_tip_toplam(OLGU, BOYUT)
print()
print("duz sonuc   :", t_duz, " birlestirme:", b_duz)
print("yildiz sonuc:", t_yil, " birlestirme:", b_yil)
print("sonuc birebir ayni:", t_duz == t_yil)
```

```
tablo                   satir  sutun  saklanan hucre
duz tablo , yedi alan    2200      7           15400
duz tablo , bes alan     2200      5           11000
olgu                     2200      3            6600
boyut abone               200      3             600
boyut donem                12      3              36
yildiz toplam                                   7236
alan dusurmenin kazandirdigi: 4400
ayrimin kazandirdigi: 3764  boyut satiri: 212

duz sonuc   : {'isyeri': 29932, 'mesken': 28522}  birlestirme: 0
yildiz sonuc: {'isyeri': 29932, 'mesken': 28522}  birlestirme: 2200
sonuc birebir ayni: True
```

Toplam düşüş **15400**'den **7236**'ya, yani **8164 hücre**. Bu sayının **4400**'ü ayrıma ait
değildir: endeks ile birim alanlarının artık taşınmaması iki sütunu, yani 2200 satırda 4400 hücreyi
düşürüyor. Aynı işlem düz tabloda da yapılabilirdi ve yapıldığında düz tablo **11000** hücreye
inerdi. Boyutsal ayrımın kendi kazancı bu satırla yıldız toplamının farkıdır: **3764 hücre**.

Bu 3764 hücre tekrarın kendisidir. Bölge ve tip alanları 2200 satırda değil 200 satırda duruyor,
dönemin yılı ve ayı 2200 satırda değil on iki satırda. Boyut tablolarının tamamı **212 satır**
tutuyor. Kazanç, olgu satırının boyut satırına oranından gelir: her abonenin on bir dönemi olduğu
için niteleyen her alan on bir kez değil bir kez yazılıyor. Bu oran birebire yaklaşsaydı, yani her
abonenin tek dönemi olsaydı, ayrımın saklama tarafında kazandıracağı bir şey kalmazdı.

Sonuç satırı ayrımın ilk sınavıdır ve geçer: iki düzen aynı sözlüğü üretiyor. Boyutsal modelleme
veriyi özetlemez, sıkıştırmaz, örneklemez; yalnızca **nerede yazıldığını** değiştirir. Sonuç
tarafında hiçbir borç bırakmaması, bundan sonraki bütün ölçümlerin tek bir eksene indirgenmesini
sağlar: aynı cevap, farklı bedel.

#### Sorgunun Ödediği Birleştirme

Bedel sonuçta değil, sonuca ulaşma yolundadır. Düz tabloda tip ile tüketim aynı satırda durduğu için
sorgu tek dosyayı okur ve hiçbir eşleme yapmaz. Olgu tablosunda tip yoktur; sorgu her olgu satırı
için boyut bağıntısına bakmak zorundadır. Üçüncü blok iki yolun dokunduğu dosyayı, okuduğu hücreyi ve
ödediği maliyeti sayar.

```python
d_abone = [{"abone": a, **v} for a, v in BOYUT["abone"].items()]
q_duz = sorgu(yaz(SATIR, ()), (), ("tip", "m3"))
q_olgu = sorgu(yaz(OLGU, ()), (), ("abone", "m3"))
q_boyut = sorgu(yaz(d_abone, ()), (), ("abone", "tip"))
print(f"{'okuma':<22}{'dosya':>7}{'hucre':>7}{'maliyet':>9}{'birlestirme':>13}")
print(f"{'duz tablo':<22}{q_duz['dosya']:>7}{q_duz['hucre']:>7}"
      f"{q_duz['maliyet']:>9}{0:>13}")
print(f"{'olgu':<22}{q_olgu['dosya']:>7}{q_olgu['hucre']:>7}{q_olgu['maliyet']:>9}{'':>13}")
print(f"{'boyut abone':<22}{q_boyut['dosya']:>7}{q_boyut['hucre']:>7}"
      f"{q_boyut['maliyet']:>9}{'':>13}")
print(f"{'olgu + boyut':<22}{q_olgu['dosya'] + q_boyut['dosya']:>7}"
      f"{q_olgu['hucre'] + q_boyut['hucre']:>7}"
      f"{q_olgu['maliyet'] + q_boyut['maliyet']:>9}{len(OLGU):>13}")
print("dosya sabiti:", DOSYA_SABIT, "hucre")
print()
print(f"{'degisiklik':<26}{'duz tablo':>11}{'boyut':>7}")
print(f"{'A007 tipi duzeltiliyor':<26}"
      f"{sum(1 for k in SATIR if k['abone'] == 'A007'):>11}{1:>7}")
print(f"{'kuzey adi degisiyor':<26}"
      f"{sum(1 for k in SATIR if k['bolge'] == 'kuzey'):>11}"
      f"{sum(1 for v in BOYUT['abone'].values() if v['bolge'] == 'kuzey'):>7}")
print()
BOYUT_SABIT = len(BOYUT["abone"]) * 3 + len(BOYUT["donem"]) * 3
print("boyutun sabit hucresi:", BOYUT_SABIT)
print(f"{'olgu satiri':>12}{'duz bes alan':>14}{'yildiz':>9}{'kazanc':>9}")
for n in (550, 1100, 2200, 4400):
    print(f"{n:>12}{5 * n:>14}{3 * n + BOYUT_SABIT:>9}{2 * n - BOYUT_SABIT:>9}")
```

```
okuma                   dosya  hucre  maliyet  birlestirme
duz tablo                   1   4400     4520            0
olgu                        1   4400     4520             
boyut abone                 1    400      520             
olgu + boyut                2   4800     5040         2200
dosya sabiti: 120 hucre

degisiklik                  duz tablo  boyut
A007 tipi duzeltiliyor             11      1
kuzey adi degisiyor               506     46

boyutun sabit hucresi: 636
 olgu satiri  duz bes alan   yildiz   kazanc
         550          2750     2286      464
        1100          5500     3936     1564
        2200         11000     7236     3764
        4400         22000    13836     8164
```

Tablonun ilk yarısı kursun kuralını bu derse indirir. Saklamada kazanılan **8164 hücre** okumada
geri ödenmiyor, üstüne konuyor: aynı soruyu yanıtlamak düz tabloda **4520**, olgu artı boyutta
**5040** maliyet istiyor. Fark **520**'dir ve iki parçadan oluşur: boyut bağıntısının 400 hücresi ve
ikinci dosyaya dokunmanın **120** hücrelik sabit bedeli. Dosya sabiti 120 seçildiği için ikinci
dosya bu kadar tutuyor; sabit daha büyük seçilseydi ayrımın okuma tarafındaki bedeli de büyürdü.

Birleştirme sütunu farkın nereden geldiğini söyler. Düz tabloda **0**, olgu artı boyutta **2200**:
her olgu satırı için boyuta bir kez bakılıyor. Bu, hattan çıkan satır sayısıyla birebir aynı sayıdır
ve tesadüf değildir; boyutsal düzenin okuma tarafındaki bedeli olgu bağıntısının satır sayısına
bağlıdır. Düzenin sayısı sakladığı satır değil, **bir sorgunun ona dokunmak için ödediğidir**; bu
sorgu için ödenen 5040'tır ve hangi sorgu olduğu yazılmadan bu sayının anlamı yoktur.

#### Değişikliğin Dokunduğu Satır

Tablonun ikinci yarısı ayrımın okuma dışındaki karşılığını verir. Bir abonenin tipi yanlış
kaydedilmişse düz tabloda o abonenin **11** satırı düzeltilir, boyutta **1** satır. Bir bölgenin adı
değişirse düz tabloda **506** satır güncellenir, boyutta **46**. İkisinde de değişen bilgi tektir;
değişen satır sayısı, o bilginin kaç kez yazıldığına eşittir.

Bu, ayrımın en somut kazancıdır ve tekrarın doğrudan sonucudur. Aynı bilgi 506 yerde duruyorsa
güncellemenin 506 yerin hepsine ulaşması gerekir; ulaşmadığı yer kalırsa aynı bölge iki adla
görünür. Boyut tablosu güncellemenin dokunduğu yüzeyi küçültür, yani tutarsızlığın oluşabileceği
yeri küçültür. Karşılığı sorgu başına 2200 birleştirmedir ve bu takas, boyutsal modellemenin
tamamıdır.

Son tablo iki bedelin ölçekle ilişkisini verir. Boyut tablolarının hücresi **636**'dır ve olgu
büyüdükçe büyümez: abone sayısı sabit kaldığı sürece 200 satır 200 satır kalır. Beş alanlı düz
tablo `n` satırda `5n` hücre tutarken yıldız `3n + 636` tutar, yani ayrımın kazancı `2n - 636`'dır.
Ölçülen 2200 satırda bu **3764** eder ve blok bunu doğrular. Sayı 550 satırda **464**'e iner, 4400
satırda **8164**'e çıkar. Okuma tarafındaki bedel ise ölçekten bağımsızdır: boyut hangi sorguda
olursa olsun bir kez okunur ve **520** tutar. Ayrımın küçük tabloda tartışmalı, büyük tabloda
kaçınılmaz görünmesinin nedeni budur; iki durumda da ölçülen şey aynı iki sayıdır.

#### Özet

- Olgu tablosu ölçülen değeri, boyut bağıntısı o değeri niteleyen bilgiyi taşır; taneciği abone ile
  dönemin çifti olan 2200 satır iki düzende de aynı veriyi anlatır.
- Saklanan hücre **15400**'den **7236**'ya iner, ama düşüşün **4400**'ü artık taşınmayan iki alana
  aittir; ayrımın kendi kazancı **3764 hücredir** ve boyut bağıntılarının tamamı **212 satır** tutar.
- İki düzenin ürettiği tip başına toplam **birebir aynıdır**; boyutsal modelleme sonucu değil,
  sonuca ulaşma yolunu değiştirir.
- Bedel okumada ödenir: aynı sorgu düz tabloda **4520**, olgu artı boyutta **5040** maliyet ister ve
  birleştirme **0**'dan **2200**'e çıkar; farkın 120'si dosya sabitidir.
- Ayrımın okuma dışındaki karşılığı güncellemedir: bir bölge adı düz tabloda **506**, boyutta **46**
  satıra dokunur, yani tutarsızlığın oluşabileceği yüzey on bir kat küçülür.

#### Sonraki Adım

Bu derste boyut bağıntıları düzleştirilmiş kuruldu: abonenin bölgesi bölge adının kendisi olarak, 200
satırın her birinde yazıldı. Bölge kümesi beş elemanlıdır, yani aynı beş metin boyut bağıntısında
kırk kez tekrarlanıyor. Düz tabloda tekrarı kaldırmak için uygulanan işlem burada da uygulanabilir:
bölgeyi ayrı bir tabloya çıkarıp boyutta yalnızca bir başvuru bırakmak. Bu, olgu ile boyut arasında
bir değil iki adımlık bir zincir kurar. Sonraki ders bu zinciri sayar: normalleştirme derecesi
arttıkça kaç satır kazanılıyor, karşılığında kaç birleştirme ödeniyor ve sonuç kımıldıyor mu.

### Yıldız ve Kar Tanesi Şeması

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/yildiz-ve-kar-tanesi-semasi

Normalleştirme derecesinin boyut tarafında kazandırdığı satırla sorgu tarafında ödettiği birleştirmenin ölçülmesi: bölge ayrı bir tabloya çıkarıldığında boyut satırı 212'den 217'ye, boyut hücresi 636'dan 646'ya çıkıyor ve birleştirme 2200'den 4400'e, yani iki katına yükseliyor, buna karşılık iki şemanın ürettiği bölge toplamı birebir aynı kalıyor; maliyet 5040'tan 5170'e çıkıyor ve farkın 120'si dosya sabitidir; bölge tablosu tek nitelik taşırken normalleştirme 10 hücre kaybettiriyor, ikinci nitelikte 185 hücre kazandırmaya başlıyor ve bir bölge adının düzeltilmesi 46 satır yerine 1 satıra dokunuyor.

Önceki ders boyut bağıntılarını düzleştirilmiş kurdu: abonenin bölgesi, bölge adının kendisi olarak
200 satırın her birinde yazıldı. Bölge kümesi beş elemanlıdır, yani aynı beş metin boyut bağıntısında
kırk kez tekrarlanıyor. Olgudan çıkarılan tekrar boyutun içinde yeniden ortaya çıktı, yalnızca on
bir kat daha küçük ölçekte.

Aynı işlemi bir kez daha uygulamak mümkündür: bölgeyi ayrı bir tabloya çıkarıp abone boyutunda
yalnızca bir başvuru bırakmak. Bu, olgu ile bölge adı arasına ikinci bir halka koyar ve şemanın
biçimini değiştirir. Olgunun çevresinde tek katmanlı boyutlar duran düzen **yıldız şeması (star
schema)**, boyutların kendi alt tablolarına dallandığı düzen **kar tanesi şeması (snowflake
schema)** adını alır. Bu ders iki şemayı aynı veriyle kurar ve normalleştirme derecesinin boyut
tarafında kaç satır, sorgu tarafında kaç birleştirme ettiğini sayar.

- **AM9.** Kaynak, hat ve depo **kurgudur**; gerçek bir ambar, göl, dosya biçimi ya da sorgu motoru
  çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **AM10.** Normalleştirmenin kuramı, işlevsel bağımlılık ve normal biçim yordamı **Veri Modelleme
  ve İlişkisel Kuram** kursunda ölçüldü ve **tekrarlanmaz**. Buradaki soru yalnızca **derecenin
  boyut satırına ve birleştirme sayısına ne ödettiğidir**.
- **AM11.** İki şema aynı olgu bağıntısını paylaşır; değişen tek şey boyutların düzenidir. Olgunun
  taneciği abone ile dönemin çiftidir ve **2200 satırdır**.
- **AM12.** Bölge tablosunun anahtarı bölge dizinidir. Anahtar bir hücredir; genişliği ölçülmez.
- **AM13.** Birleştirme sayısı sorgunun boyut bağıntılarına toplam kaç kez baktığıdır. Kar tanesinde
  her olgu satırı için önce abone boyutuna, sonra bölge tablosuna bakılır.
- **AM14.** Maliyet `dosya x 120 + hücre` biçiminde okunur. **Dosya sabiti 120 bir seçimdir** ve
  ayrı bir boyut bağıntısı en az bir dosya demektir.
- **AM15.** Taban çizgisi **yıldız şemasıdır**: iki boyut bağıntısı, tek katman, dallanma yok.
- **AM16.** Çözünürlük: satır oranının adımı **0,0005**. **Toplamlar oran olarak değil, değerin
  kendisi olarak yazılır.**

#### İki Şemanın Kurulması

İlk blok ortak tanımı kurar. `yildiz` işlevi önceki dersteki düzeni verir: olgu üç alan taşır, abone
boyutu bölge adını ve tipi taşır. `kar_tanesi` aynı veriden başlar, abone boyutundaki bölge adını
bölge dizinine çevirir ve beş satırlık bir bölge tablosu açar. `sorgu_bolge_toplam` iki düzende de
aynı soruyu yanıtlar ve yol boyunca kaç kez boyuta baktığını sayar; kar tanesinde her satır için bir
bakış fazladan yapılır, çünkü abone boyutundan çıkan dizin bir ad değildir.

```python
# KURGUDUR. Sayac okumalari, hat ve depo ders icinde tanimlanir; gercek bir
# ambar, gol, dosya bicimi ya da sorgu motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


SUTUN = ("abone", "donem", "bolge", "tip", "endeks", "birim", "m3")
DOSYA_SABIT = 120          # her dosyaya dokunmanin sabit bedeli , hucre cinsinden


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def yaz(kayitlar, bolumleme=()):
    duzen = {}
    for k in kayitlar:
        anahtar = tuple(k[a] for a in bolumleme)
        duzen.setdefault(anahtar, []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None, sutunlu=True):
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    kalan = {a: v for a, v in suzgec.items() if a not in bolumleme}
    dosya, hucre, satir = 0, 0, 0
    okunan_sutun = len(secilen) if sutunlu else len(SUTUN)
    for anahtar, kayit in duzen.items():
        if budayan:
            eslesme = dict(zip(bolumleme, anahtar))
            if any(eslesme[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * okunan_sutun
        satir += sum(1 for k in kayit if all(k[a] == v for a, v in kalan.items()))
    return {"dosya": dosya, "hucre": hucre, "eslesen_satir": satir,
            "maliyet": dosya * DOSYA_SABIT + hucre}


def yildiz(kayitlar):
    """Duzlestirilmis boyutlar: bolge adi abone boyutunun icinde durur."""
    olgu = [{"abone": k["abone"], "donem": k["donem"], "m3": k["m3"]} for k in kayitlar]
    d_abone = {k["abone"]: {"bolge": k["bolge"], "tip": k["tip"]} for k in kayitlar}
    d_donem = {d: {"yil": d[:4], "ay": d[5:]} for d in DONEM}
    return olgu, {"abone": d_abone, "donem": d_donem}


def kar_tanesi(kayitlar):
    """Ayni veri , bolge ayri bir tabloya cikarilmis."""
    olgu, boy = yildiz(kayitlar)
    d_abone = {a: {"bolge_no": BOLGE.index(v["bolge"]), "tip": v["tip"]}
               for a, v in boy["abone"].items()}
    d_bolge = {i: {"ad": b} for i, b in enumerate(BOLGE)}
    return olgu, {"abone": d_abone, "bolge": d_bolge, "donem": boy["donem"]}


def sorgu_bolge_toplam(olgu, boy, kar=False):
    """Bolge basina toplam. Kar tanesinde bir birlestirme fazla."""
    birlestirme, top = 0, {}
    for o in olgu:
        a = boy["abone"][o["abone"]]
        birlestirme += 1
        if kar:
            b = boy["bolge"][a["bolge_no"]]["ad"]
            birlestirme += 1
        else:
            b = a["bolge"]
        top[b] = top.get(b, 0) + o["m3"]
    return top, birlestirme
```

#### Kazanılan Satır, Ödenen Birleştirme

İkinci blok iki şemayı kurar, boyut tarafını sayar ve aynı sorguyu ikisine de sorar.

```python
SATIR = satirlar()
Y_OLGU, Y_BOYUT = yildiz(SATIR)
K_OLGU, K_BOYUT = kar_tanesi(SATIR)
y_top, y_bir = sorgu_bolge_toplam(Y_OLGU, Y_BOYUT)
k_top, k_bir = sorgu_bolge_toplam(K_OLGU, K_BOYUT, kar=True)
y_satir = len(Y_BOYUT["abone"]) + len(Y_BOYUT["donem"])
k_satir = len(K_BOYUT["abone"]) + len(K_BOYUT["bolge"]) + len(K_BOYUT["donem"])
y_hucre = len(Y_BOYUT["abone"]) * 3 + len(Y_BOYUT["donem"]) * 3
k_hucre = len(K_BOYUT["abone"]) * 3 + len(K_BOYUT["bolge"]) * 2 + len(K_BOYUT["donem"]) * 3
print(f"{'sema':<12}{'boyut baginti':>15}{'boyut satiri':>14}{'boyut hucresi':>15}"
      f"{'birlestirme':>13}")
print(f"{'yildiz':<12}{2:>15}{y_satir:>14}{y_hucre:>15}{y_bir:>13}")
print(f"{'kar tanesi':<12}{3:>15}{k_satir:>14}{k_hucre:>15}{k_bir:>13}")
print("sonuc birebir ayni:", y_top == k_top)
print("sonuc:", y_top)
```

```
sema          boyut baginti  boyut satiri  boyut hucresi  birlestirme
yildiz                    2           212            636         2200
kar tanesi                3           217            646         4400
sonuc birebir ayni: True
sonuc: {'merkez': 14536, 'kuzey': 13338, 'dogu': 9695, 'bati': 9164, 'guney': 11721}
```

Tablo dersin tamamını taşır. Boyut satırı **212**'den **217**'ye çıkıyor: beş satır, bölge
tablosunun kendisi. Boyut hücresi **636**'dan **646**'ya çıkıyor. Birleştirme ise **2200**'den
**4400**'e, yani **iki katına** yükseliyor. Sonuç satırı ikisinin de aynı sözlüğü ürettiğini söyler.

Buradaki asıl bulgu, beklenenin tersi olmasıdır. Normalleştirme tekrarı kaldırdığı için yer
kazandırmalıydı; bu ölçümde **kazandırmıyor**. Abone boyutunda bölge adının yerini bölge dizini
alıyor, ama sütun sayısı değişmiyor: 200 satır hâlâ üç hücre taşıyor. Kazanılan tek şey adın metin
yerine dizin olmasıdır ve bu ölçüde bir hücre bir hücredir. Buna karşılık beş satırlık bölge tablosu
ile onun on hücresi baştan eklenmiş oluyor. Beş satır ve on hücre eksi yönde, birleştirme iki katına
çıkacak biçimde artı yönde.

Sonucun birebir aynı kalması, bu takasın **yalnızca bedel tarafında** olduğunu gösterir. Kar tanesi
şeması yıldız şemasının veremediği bir cevabı vermiyor, aynı cevabı iki kat birleştirmeyle veriyor.
Kursun kuralı bu satırı okur: düzenin sayısı sakladığı satır değil, **bir sorgunun ona dokunmak için
ödediğidir**; bu sorgu için ödenen 4400 birleştirmedir ve hangi sorgu olduğu yazılmadan bu sayının
anlamı yoktur.

Bu ölçüm aynı zamanda kursun ikinci iddiasının bu derse düşen biçimidir: **düzen kararı işi
kötüleştirebilir.** Tek nitelik taşıyan bir bölge tablosuyla kurulan kar tanesi on hücre
kaybettiriyor, birleştirmeyi ikiye katlıyor ve karşılığında hiçbir yeni cevap vermiyor.
Kötüleşmenin fark edilmemesinin nedeni de aynı satırdadır: sonuç kımıldamıyor.

#### Maliyet, Güncelleme ve Ödemenin Başladığı Nokta

Üçüncü blok üç şeyi arka arkaya ölçer: aynı sorgunun iki şemada dokunduğu dosya ve ödediği maliyet,
bir bölge adı düzeltildiğinde güncellenen satır sayısı ve bölge tablosunun taşıdığı nitelik sayısı
arttıkça hücre dengesinin nereye gittiği.

```python
y_ab = [{"abone": a, **v} for a, v in Y_BOYUT["abone"].items()]
k_ab = [{"abone": a, **v} for a, v in K_BOYUT["abone"].items()]
k_bl = [{"bolge_no": i, **v} for i, v in K_BOYUT["bolge"].items()]
q_olgu = sorgu(yaz(Y_OLGU, ()), (), ("abone", "m3"))
q_yab = sorgu(yaz(y_ab, ()), (), ("abone", "bolge"))
q_kab = sorgu(yaz(k_ab, ()), (), ("abone", "bolge_no"))
q_kbl = sorgu(yaz(k_bl, ()), (), ("bolge_no", "ad"))
y = [q_olgu, q_yab]
k = [q_olgu, q_kab, q_kbl]
print(f"{'sema':<12}{'dosya':>7}{'hucre':>7}{'maliyet':>9}")
for ad, q in (("yildiz", y), ("kar tanesi", k)):
    print(f"{ad:<12}{sum(x['dosya'] for x in q):>7}{sum(x['hucre'] for x in q):>7}"
          f"{sum(x['maliyet'] for x in q):>9}")
print("fark:", sum(x["maliyet"] for x in k) - sum(x["maliyet"] for x in y),
      " bunun", DOSYA_SABIT, "kadari dosya sabiti")
print()
n_ab = sum(1 for v in Y_BOYUT["abone"].values() if v["bolge"] == "kuzey")
print("bir bolgenin adi degisiyor: yildiz", n_ab, "satir , kar tanesi 1 satir")
print()
print(f"{'bolge niteligi':>15}{'yildiz hucre':>14}{'kar tanesi hucre':>18}{'fark':>7}")
for n in range(1, 7):
    y_h = len(Y_BOYUT["abone"]) * (2 + n)
    k_h = len(K_BOYUT["abone"]) * 3 + len(K_BOYUT["bolge"]) * (1 + n)
    print(f"{n:>15}{y_h:>14}{k_h:>18}{y_h - k_h:>7}")
```

```
sema          dosya  hucre  maliyet
yildiz            2   4800     5040
kar tanesi        3   4810     5170
fark: 130  bunun 120 kadari dosya sabiti

bir bolgenin adi degisiyor: yildiz 46 satir , kar tanesi 1 satir

 bolge niteligi  yildiz hucre  kar tanesi hucre   fark
              1           600               610    -10
              2           800               615    185
              3          1000               620    380
              4          1200               625    575
              5          1400               630    770
              6          1600               635    965
```

Maliyet satırı beklenenden küçük bir fark verir: **5040**'a karşı **5170**, aradaki **130**. Bunun
**120**'si üçüncü dosyaya dokunmanın sabit bedelidir, kalan **10** bölge tablosunun on hücresi.
Okunan hücre neredeyse hiç artmıyor, çünkü bölge tablosu beş satırlıktır. Dosya sabiti 120 seçildiği
için ek tablonun bedeli bu kadar; sabit daha büyük seçilseydi kar tanesinin okuma tarafındaki bedeli
neredeyse tamamen sabitten gelirdi. Birleştirmenin iki katına çıkması bu ölçüde görünmez, çünkü
birleştirme dosyaya dokunmaz; sayılan iki şey ayrı bedellerdir ve ikisi de yazılmadan şema
ölçülmemiş sayılır.

Güncelleme satırı normalleştirmenin gerçekten aldığı karşılıktır. Bir bölgenin adı düzeltildiğinde
yıldız şemasında **46** abone satırı güncellenir, kar tanesinde **1** satır. Yıldızda ad kırk altı
yerde durduğu için güncellemenin kırk altı yere ulaşması gerekir; kar tanesinde adın tek bir kopyası
vardır ve başka bir yerde bulunamaz. Kazanç saklamada değil, **tutarsızlığın oluşabileceği yüzeyde**
ortaya çıkıyor.

İki tarafın ölçekle ilişkisi zıttır ve şema kararı bu zıtlıktan çıkar. Kar tanesinin ödettiği
fazladan birleştirme olgu satırı başına birdir: 2200 satırda 2200 fazla, olgu iki katına çıksaydı
4400 fazla. Kazandırdığı güncelleme yüzeyi ise olgudan bağımsızdır; bölgenin adı **46** abone
satırında duruyor ve olgu ne kadar büyürse büyüsün abone sayısı değişmediği sürece 46'da kalıyor.
Yani veri büyüdükçe dallanmanın bedeli büyür, kazancı yerinde sayar. Karar bu yüzden boyutun kaç kez
tekrarlandığına değil, **olgu satırının boyut satırına oranına** bakılarak verilir; bu ölçümde oran
on birdir ve her birleştirme o oranla çarpılarak ödenir.

Son tablo dengenin nereye gittiğini söyler. Bölge tablosu tek nitelik taşırken normalleştirme **10
hücre kaybettiriyor**. İkinci nitelikte tablo dönüyor: yıldız 800, kar tanesi 615, aradaki fark
**185 hücre** kar tanesi lehine. Üçüncü nitelikte fark **380**'e, altıncıda **965**'e çıkıyor.
Eşiğin nerede olduğu tekrar sayısına bağlıdır: 200 abone satırı, 5 bölge satırı ve niteliğin abone
başına bir kez yazılması. Bölgenin taşıdığı bilgi tek bir addan ibaretse normalleştirme
ödemiyor; bölge birkaç nitelik taşımaya başladığı anda ödemeye başlıyor ve birleştirme sayısı
buradan bağımsız olarak iki katında kalıyor.

#### Özet

- Yıldız şeması boyutları düzleştirilmiş tutar, kar tanesi şeması boyutu kendi alt tablosuna
  dallandırır; iki şema aynı olgu bağıntısını ve aynı 2200 satırı paylaşır.
- Bölge ayrı bir tabloya çıkarıldığında boyut satırı **212**'den **217**'ye, boyut hücresi
  **636**'dan **646**'ya çıkar ve birleştirme **2200**'den **4400**'e, yani iki katına yükselir.
- İki şemanın ürettiği bölge toplamı **birebir aynıdır**; kar tanesi yeni bir cevap vermez, aynı
  cevabı iki kat birleştirmeyle verir.
- Maliyet **5040**'tan **5170**'e çıkar ve farkın **120**'si dosya sabitidir; birleştirmenin
  ikiye katlanması maliyette görünmez, ayrı bir bedeldir ve ayrı yazılır.
- Normalleştirmenin aldığı karşılık güncelleme yüzeyidir: bir bölge adının düzeltilmesi yıldızda
  **46**, kar tanesinde **1** satıra dokunur.
- Hücre dengesi bölge tablosunun ikinci niteliğinde döner: tek nitelikte kar tanesi **10** hücre
  kaybettirir, iki nitelikte **185**, altı nitelikte **965** hücre kazandırır.

#### Sonraki Adım

İki şema da bu derste sessiz bir varsayımla çalıştı: abonenin bölgesi ve tipi **değişmez**. Boyut
tablosu abone başına tek satır tutuyor, o satır abonenin bilgisini veriyor ve olgunun hangi dönemine
bakıldığı bu bilgiyi etkilemiyor. Gerçek boyutlar böyle davranmaz. Bir mesken işyerine dönüşür, bir
abone başka bölgeye taşınır, bir sınıflandırma yeniden yapılır. Boyut satırı bir kez üzerine
yazıldığında geçmiş dönemlerin olguları da yeni bilgiyle nitelenir ve sorgu bunu hiçbir yerde
bildirmez. Sonraki ders bu değişimi ölçer: tarihçe tutmayan bir boyut sorguyu ne kadar
yavaşlatıyor, cevabı ne kadar değiştiriyor ve tarihçe tutmanın boyut satırına bedeli ne oluyor.

### Yavaş Değişen Boyutlar

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/yavas-degisen-boyutlar

Tarihçe tutmayan bir boyutun sorguyu yavaşlatmadan cevabı yanlışladığının ölçülmesi: yirmi abonenin tipi bir dönemde değiştiğinde üzerine yazan boyut geçmiş dönemleri de bugünün tipiyle niteliyor, 100 olgu satırı yanlış kovaya düşüyor ve işyeri toplamı 33145 yerine 35893 çıkarak 2748 metreküpü yanlış kovaya gönderiyor; iki kovanın toplamı 58454'te aynı kaldığı, olgu satırı hiç değişmediği ve birleştirme sayısı 2200'de sabit durduğu için şema, tür, tekillik ve satır sayısı denetimlerinin dördü de yeşil kalıyor, dağılım denetimi ise 0,25 bandında görmüyor; tarihçe tutmanın bedeli boyut satırının 200'den 2200'e ve maliyetin 7240'tan 13440'a çıkmasıdır, geçerlilik aralığı ise aynı doğru cevabı 220 satır ve 7500 maliyetle veriyor.

Önceki iki ders boyut bağıntılarını sessiz bir varsayımla kurdu: abonenin bölgesi ve tipi değişmez.
Boyut abone başına tek satır tuttu, o satır abonenin bilgisini verdi ve olgunun hangi dönemine
bakıldığı bu bilgiyi hiç etkilemedi. Ölçülen bütün sayılar bu varsayımın üstünde duruyor.

Gerçek boyutlar böyle davranmaz. Bir mesken işyerine dönüşür, bir abone başka bölgeye taşınır, bir
sınıflandırma yeniden yapılır. Bu değişimler olgu satırları kadar sık değildir, bu yüzden **yavaş
değişen boyut (slowly changing dimension)** adını alırlar. Yavaşlık onları önemsiz yapmaz: boyut
satırı bir kez üzerine yazıldığında geçmiş dönemlerin olguları da yeni bilgiyle nitelenir. Bu ders o
anı ölçer ve kursun en rahatsız edici sonucunu verir. Üzerine yazan boyut sorguyu **yavaşlatmaz**,
**cevabı yanlışlar**.

- **AM17.** Kaynak, hat ve depo **kurgudur**; gerçek bir ambar, göl, dosya biçimi ya da sorgu motoru
  çağrılmaz. Tohum **20260218**, ölçü koşumdan bağımsızdır.
- **AM18.** Değişim kurgudur ve bir kaza değildir: `A001`–`A050` aralığındaki mesken aboneler
  `2026-07` döneminden başlayarak işyeri sayılır. Bu, sıradan bir sınıflandırma güncellemesidir.
- **AM19.** Olgu satırı **2200**'dür ve değişimden **etkilenmez**; değişen tek şey boyut bağıntısıdır.
- **AM20.** **Üzerine yazan boyut** abone başına tek satır tutar ve son değeri saklar. **Tarihçe
  tutan boyut** abone ile dönemin her çifti için bir satır tutar.
- **AM21.** Maliyet `dosya x 120 + hücre` biçiminde okunur. **Dosya sabiti 120 bir seçimdir.**
- **AM22.** Denetimlerin tanımı **Veri Mühendisliğine Giriş** kursunda kuruldu ve **yordamı
  tekrarlanmaz**; burada yalnızca **hangisinin ateşlediği** sorulur.
- **AM23.** Taban çizgisi **üzerine yazan boyuttur**: tarihçe yok, boyut satırı abone sayısı kadar.
- **AM24.** Çözünürlük: satır oranının adımı **0,0005**. **Toplamlar oran değil, değerin kendisi
  olarak yazılır**; oran yalnızca dağılım bandı için basılır.

#### Değişimin Kurulması

İlk blok ortak tanımı kurar ve üzerine üç parça ekler. `tip_degisimi` değişimi olgu satırlarına
işler: `2026-07` ve sonrasındaki satırlarda ilgili abonelerin tipi işyeri olur, önceki dönemlerde
mesken kalır. Yani **doğru tarihçe olgu satırlarının içindedir**. `scd1` bu satırlardan üzerine
yazan boyutu üretir; sözlük abone anahtarını taşıdığı için her abone için **son görülen** tip kalır.
`scd2` tarihçe tutan boyutu üretir ve anahtarı abone ile dönemin çiftidir. `tip_basina_toplam` ikisi
arasında yalnızca hangi anahtarla arandığı bakımından ayrılır.

```python
# KURGUDUR. Sayac okumalari, hat ve boyut ders icinde tanimlanir; gercek bir
# ambar, gol, dosya bicimi ya da sorgu motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def kaynak_uret():
    kayit = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            kayit.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                          "endeks": e, "birim": "m3"})
    return kayit


def a1_ayikla(kayit):
    ZOR = ("abone", "donem", "bolge", "endeks", "birim")
    tut = [k for k in kayit if all(a in k for a in ZOR)]
    return tut, len(kayit) - len(tut)


def a2_tekille(kayit):
    gor, tut = set(), []
    for k in kayit:
        ad = (k["abone"], k["donem"])
        if ad in gor:
            continue
        gor.add(ad)
        tut.append(k)
    return tut, len(kayit) - len(tut)


def a3_tuketim(kayit):
    tablo = {(k["abone"], k["donem"]): k for k in kayit}
    cikti, dusen = [], 0
    for k in kayit:
        i = DONEM.index(k["donem"])
        if i == 0:
            continue
        onceki = tablo.get((k["abone"], DONEM[i - 1]))
        if onceki is None:
            dusen += 1
            continue
        try:
            f = int(k["endeks"]) - int(onceki["endeks"])
        except (TypeError, ValueError):
            dusen += 1
            continue
        cikti.append(dict(k, m3=f))
    return cikti, dusen


SUTUN = ("abone", "donem", "bolge", "tip", "endeks", "birim", "m3")
DOSYA_SABIT = 120          # her dosyaya dokunmanin sabit bedeli , hucre cinsinden


def satirlar():
    v, _ = a1_ayikla(kaynak_uret())
    v, _ = a2_tekille(v)
    v, _ = a3_tuketim(v)
    return v


def yaz(kayitlar, bolumleme=()):
    duzen = {}
    for k in kayitlar:
        anahtar = tuple(k[a] for a in bolumleme)
        duzen.setdefault(anahtar, []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None, sutunlu=True):
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    kalan = {a: v for a, v in suzgec.items() if a not in bolumleme}
    dosya, hucre, satir = 0, 0, 0
    okunan_sutun = len(secilen) if sutunlu else len(SUTUN)
    for anahtar, kayit in duzen.items():
        if budayan:
            eslesme = dict(zip(bolumleme, anahtar))
            if any(eslesme[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * okunan_sutun
        satir += sum(1 for k in kayit if all(k[a] == v for a, v in kalan.items()))
    return {"dosya": dosya, "hucre": hucre, "eslesen_satir": satir,
            "maliyet": dosya * DOSYA_SABIT + hucre}


def tip_degisimi(kayitlar, donem="2026-07", abone_ust="A050"):
    """A001..A050 arasindaki mesken aboneler bu donemde isyeri oluyor."""
    yeni = []
    for k in kayitlar:
        if k["abone"] <= abone_ust and k["donem"] >= donem and k["tip"] == "mesken":
            yeni.append(dict(k, tip="isyeri"))
        else:
            yeni.append(dict(k))
    return yeni


def scd1(kayitlar):
    """Uzerine yaz: boyut yalnizca son degeri tutar."""
    return {k["abone"]: k["tip"] for k in kayitlar}


def scd2(kayitlar):
    """Tarihce tut: (abone , donem) -> tip."""
    return {(k["abone"], k["donem"]): k["tip"] for k in kayitlar}


def tip_basina_toplam(kayitlar, boyut, tarihli):
    top = {}
    for k in kayitlar:
        t = boyut[(k["abone"], k["donem"])] if tarihli else boyut[k["abone"]]
        top[t] = top.get(t, 0) + k["m3"]
    return top
```

#### Yanlış Kovaya Giden Metreküp

İkinci blok iki boyutu kurar, boyut tarafını sayar ve aynı soruyu ikisine de sorar: tip başına
toplam tüketim ne kadar.

```python
SATIR = satirlar()
GERCEK = tip_degisimi(SATIR)
B1, B2 = scd1(GERCEK), scd2(GERCEK)
degisen = {k["abone"] for k in GERCEK if ABONE[k["abone"]]["tip"] != k["tip"]}
yanlis_satir = sum(1 for k in GERCEK if B1[k["abone"]] != k["tip"])
T1 = tip_basina_toplam(GERCEK, B1, False)
T2 = tip_basina_toplam(GERCEK, B2, True)
print("olgu satiri:", len(GERCEK), " tipi degisen abone:", len(degisen))
print(f"{'boyut':<20}{'satir':>7}{'hucre':>7}{'birlestirme':>13}")
print(f"{'uzerine yazan':<20}{len(B1):>7}{len(B1) * 2:>7}{len(GERCEK):>13}")
print(f"{'tarihce tutan':<20}{len(B2):>7}{len(B2) * 3:>7}{len(GERCEK):>13}")
print()
print("uzerine yazan sonuc:", T1, " toplam", sum(T1.values()))
print("tarihce tutan sonuc:", T2, " toplam", sum(T2.values()))
print("yanlis kovaya giden:", T1["isyeri"] - T2["isyeri"], "m3")
print("yanlis nitelenen olgu satiri:", yanlis_satir)
```

```
olgu satiri: 2200  tipi degisen abone: 20
boyut                 satir  hucre  birlestirme
uzerine yazan           200    400         2200
tarihce tutan          2200   6600         2200

uzerine yazan sonuc: {'isyeri': 35893, 'mesken': 22561}  toplam 58454
tarihce tutan sonuc: {'isyeri': 33145, 'mesken': 25309}  toplam 58454
yanlis kovaya giden: 2748 m3
yanlis nitelenen olgu satiri: 100
```

Sonuç satırları dersin tamamıdır. Doğru cevap işyeri kovasında **33145**, mesken kovasında
**25309**'dur. Üzerine yazan boyutla çalışan sorgu işyerini **35893**, meskeni **22561** verir.
Aradaki **2748 metreküp** kaybolmuş değildir; yanlış kovadadır. İki kovanın toplamı iki kurulumda da
**58454**'tür, birebir aynı.

Mekanizma tek cümleyle yazılır. Tipi değişen **20 abonenin** `2026-02` ile `2026-06` arasındaki
**100 olgu satırı** o dönemlerde mesken tüketimiydi; üzerine yazan boyut yalnızca son değeri
tuttuğu için sorgu bu yüz satırı da işyeri sayıyor. Geçmiş, bugünün bilgisiyle yeniden nitelendi.
Boyut satırı yanlış değildir: `A007` bugün gerçekten işyeridir. Yanlış olan, **bugünün doğrusunun
geçmişe uygulanmasıdır**.

Birleştirme sütunu iddianın ikinci yarısını verir. İki kurulumda da **2200** birleştirme yapılıyor.
Üzerine yazan boyutla çalışan sorgu bir adım bile fazla iş yapmıyor, hatta okuduğu boyut **400
hücre**, ötekininki **6600**. Yanlış cevap veren kurulum **daha hızlı ve daha ucuzdur**. Sorguya
bakarak, plana bakarak, süreye bakarak bu hata bulunamaz, çünkü ortada bir yavaşlama yoktur.

#### Hiçbir Denetimin Görmediği

Üçüncü blok, önceki kursta tanımlanan denetimleri bu kuruluma uygular. Dördü de olgu satırları
üzerinde koşar: alan, tür, tekillik ve satır sayısı. Ardından dağılım denetimi sunulan iki kovayı
doğru cevapla karşılaştırır.

```python
def d_sema(v, alanlar=("abone", "donem", "bolge", "endeks", "birim")):
    return any(any(a not in k for a in alanlar) for k in v)


def d_tip(v):
    return any(not isinstance(k.get("endeks"), int) for k in v)


def d_tekillik(v):
    ad = [(k["abone"], k["donem"]) for k in v]
    return len(ad) != len(set(ad))


def d_satir_sayisi(v, beklenen=2200, pay=0.05):
    return abs(len(v) - beklenen) / beklenen > pay


def d_dagilim(ozet, taban, pay=0.25):
    for ad, v in ozet.items():
        t = taban.get(ad)
        if t and abs(v - t) / max(abs(t), 1) > pay:
            return True
    return False


print("olgu satirlari uzerinde kosan denetimler")
for ad, sonuc in (("sema", d_sema(GERCEK)), ("tip", d_tip(GERCEK)),
                  ("tekillik", d_tekillik(GERCEK)),
                  ("satir_sayisi", d_satir_sayisi(GERCEK))):
    print(f"  {ad:<14}{'atesledi' if sonuc else 'yesil'}")
print("iki kurulumun okudugu olgu satiri:", len(GERCEK), "ve", len(GERCEK))
print("iki kovanin toplami:", sum(T1.values()), "ve", sum(T2.values()))
print()
print("dagilim denetimi , tarihce tutan sonuca karsi")
for pay in (0.25, 0.15, 0.10, 0.05):
    print(f"  pay {pay:.2f}   {'atesledi' if d_dagilim(T1, T2, pay) else 'yesil'}")
print(f"{'kova':<10}{'tarihceli':>10}{'uzerine yazan':>15}{'sapma':>8}")
for kova in sorted(T2):
    print(f"{kova:<10}{T2[kova]:>10}{T1[kova]:>15}"
          f"{abs(T1[kova] - T2[kova]) / T2[kova]:>8.4f}")
```

```
olgu satirlari uzerinde kosan denetimler
  sema          yesil
  tip           yesil
  tekillik      yesil
  satir_sayisi  yesil
iki kurulumun okudugu olgu satiri: 2200 ve 2200
iki kovanin toplami: 58454 ve 58454

dagilim denetimi , tarihce tutan sonuca karsi
  pay 0.25   yesil
  pay 0.15   yesil
  pay 0.10   atesledi
  pay 0.05   atesledi
kova       tarihceli  uzerine yazan   sapma
isyeri         33145          35893  0.0829
mesken         25309          22561  0.1086
```

Dört denetimin dördü de yeşildir ve bu bir kusur değildir. Denetimler olgu satırlarına bakar, olgu
satırları **kusursuzdur**: 2200 satır, eksik alan yok, tür kayması yok, yinelenen anahtar yok. Hata
satırların içinde değil, satırla boyut arasındaki **birleştirmenin içindedir**. Veri kalitesinin
satır düzeyinde tanımlanan hiçbir boyutu bunu göremez, çünkü ölçtükleri yerde bir bozulma yoktur.

Dağılım denetimi tek adaydır ve onun da iki koşulu vardır. Birincisi bir taban gerektirmesidir; bu
ölçümde taban tarihçe tutan kurulumun cevabıdır, yani **atılan tarihçe**. Üzerine yazan boyutu seçen
bir kurulumda o taban zaten yoktur, çünkü onu üretecek satırlar saklanmamıştır. İkincisi banttır:
sapma işyerinde **0,0829**, meskende **0,1086**. Alışılmış **0,25** bandı bunu görmez; band 0,10'a
indirildiğinde ateşler, ama o bandda düzgün hattın olağan dalgalanması da ateşleyecektir.

Bu, önceki kursun **sunulan sayı sessizce yanlış olabilir** iddiasının depolama tarafındaki
karşılığıdır ve iki taraf arasındaki fark önemlidir. Orada bozulma üretici tarafında oluyordu ve
hattın bir aşaması onu yutuyordu. Burada **hiçbir bozulma yoktur**: kaynak doğru, hat doğru, satır
doğru. Yanlış olan tek şey **düzen kararıdır** ve o karar bir tabloya kaç satır yazılacağıdır.

#### Tarihçenin Ucuz Biçimi

Doğru cevabın bedeli tabloda görünüyor: tarihçe tutan boyut **2200 satır** taşıyor, üzerine yazan
**200**. On bir kat. Bu, tarihçe tutmanın zorunlu bedeli değildir. Tarihçe tutan boyut her dönem
için bir satır yazıyor, oysa yirmi abone dışında hiçbir şey değişmiyor. Dördüncü blok üçüncü bir
biçimi ölçer: boyut yalnızca **değişim noktalarını** yazar ve her satır o değerin hangi dönemden
itibaren geçerli olduğunu taşır.

```python
def scd_aralik(kayitlar):
    """Gecerlilik araligi: abone basina yalnizca degisim noktalari yazilir."""
    son, boyut = {}, []
    for k in sorted(kayitlar, key=lambda k: (k["abone"], k["donem"])):
        if son.get(k["abone"]) != k["tip"]:
            boyut.append({"abone": k["abone"], "tip": k["tip"], "gecerli": k["donem"]})
            son[k["abone"]] = k["tip"]
    return boyut


def aralik_topla(kayitlar, boyut):
    dizin = {}
    for s in boyut:
        dizin.setdefault(s["abone"], []).append(s)
    top, birlestirme = {}, 0
    for k in kayitlar:
        t = None
        for s in dizin[k["abone"]]:
            birlestirme += 1
            if s["gecerli"] <= k["donem"]:
                t = s["tip"]
        top[t] = top.get(t, 0) + k["m3"]
    return top, birlestirme


ARALIK = scd_aralik(GERCEK)
T3, b3 = aralik_topla(GERCEK, ARALIK)
olgu = [{"abone": k["abone"], "donem": k["donem"], "m3": k["m3"]} for k in GERCEK]
q_olgu = sorgu(yaz(olgu, ()), (), ("abone", "donem", "m3"))
print(f"{'boyut':<20}{'satir':>7}{'hucre':>7}{'maliyet':>9}{'birlestirme':>13}")
for ad, kayit, secilen, bir in (
        ("uzerine yazan", [{"abone": a, "tip": t} for a, t in B1.items()],
         ("abone", "tip"), len(GERCEK)),
        ("tarihce tutan", [{"abone": a, "donem": d, "tip": t} for (a, d), t in B2.items()],
         ("abone", "donem", "tip"), len(GERCEK)),
        ("gecerlilik araligi", ARALIK, ("abone", "tip", "gecerli"), b3)):
    q = sorgu(yaz(kayit, ()), (), secilen)
    print(f"{ad:<20}{len(kayit):>7}{q['hucre']:>7}"
          f"{q['maliyet'] + q_olgu['maliyet']:>9}{bir:>13}")
print("olgu okumasi her ucunde ayni:", q_olgu["maliyet"], " dosya sabiti:", DOSYA_SABIT)
print("gecerlilik araligi sonucu:", T3)
print("tarihce tutan sonuca esit:", T3 == T2)
```

```
boyut                 satir  hucre  maliyet  birlestirme
uzerine yazan           200    400     7240         2200
tarihce tutan          2200   6600    13440         2200
gecerlilik araligi      220    660     7500         2420
olgu okumasi her ucunde ayni: 6720  dosya sabiti: 120
gecerlilik araligi sonucu: {'isyeri': 33145, 'mesken': 25309}
tarihce tutan sonuca esit: True
```

Üçüncü satır dersin çıkış yoludur. Geçerlilik aralığı **220 satır** tutuyor: 200 abonenin ilk
değeri, artı tipi değişen yirmi abonenin ikinci satırı. Ürettiği sonuç tarihçe tutan boyutunkiyle
**birebir aynıdır**, yani doğrudur. Maliyet **7500**'dür; üzerine yazan boyutun **7240**'ının
yalnızca **260** üstünde, tarihçe tutanın **13440**'ının çok altında. Bedelin kalanı birleştirmede
görünür: **2420**, çünkü iki satırlı abonelerde iki bakış yapılıyor.

Kursun kuralı bu tabloyu okur: düzenin sayısı sakladığı satır değil, **bir sorgunun ona dokunmak
için ödediğidir**. Üç boyut biçimi aynı olgu bağıntısını paylaşıyor, aynı 2200 satırı okuyor ve
**6720** maliyetlik olgu okumasını her birinde aynen ödüyor. Ayrıldıkları yer boyut tarafıdır ve
orada ödenen fark, doğru cevabın fiyatıdır. Bu fiyat **260**'tır. Üzerine yazan boyutu seçmek bu
ölçümde **260 maliyet biriminden tasarruf etmek** için 2748 metreküpü yanlış kovaya göndermektir ve
hangi sorgu için seçildiği yazılmadığında bu takas hiç görünmez.

#### Özet

- Yavaş değişen bir boyut üzerine yazıldığında geçmiş dönemlerin olguları bugünün değeriyle
  nitelenir; yirmi abonenin tipi değiştiğinde **100 olgu satırı** yanlış kovaya düşer.
- Sonuç sessizce yanlışlanır: işyeri toplamı **33145** yerine **35893** çıkar, **2748 metreküp**
  yanlış kovaya gider ve iki kovanın toplamı **58454**'te birebir aynı kalır.
- Yanlış cevap veren kurulum daha ucuzdur: birleştirme iki kurulumda da **2200**, okunan boyut
  hücresi **400**'e karşı **6600**. Sorguya, plana ya da süreye bakarak bu hata bulunamaz.
- Satır düzeyinde koşan şema, tür, tekillik ve satır sayısı denetimlerinin dördü de **yeşildir**,
  çünkü olgu satırlarında hiçbir bozulma yoktur; hata birleştirmenin içindedir.
- Dağılım denetimi tek adaydır ama hem atılan tarihçeyi taban olarak ister hem de **0,25** bandında
  görmez: sapma işyerinde **0,0829**, meskende **0,1086**.
- Tarihçenin bedeli boyut satırının **200**'den **2200**'e, maliyetin **7240**'tan **13440**'a
  çıkmasıdır; geçerlilik aralığı aynı doğru cevabı **220 satır** ve **7500** maliyetle verir.

#### Sonraki Adım

Üç derste tartışılan şey tablonun **şekliydi**: hangi alan hangi tabloda duruyor, kaç tablo var,
boyut kaç satır tutuyor. Her ölçümde saklanan hücre ve okunan hücre sayıldı, ama hücrenin diske
**nasıl indiği** hiç sorulmadı. Sorgu bir tabloyu okurken satırların tamamını mı alıyor yoksa
yalnızca seçtiği sütunları mı, bu seçim okunan hücreyi kaça bölüyor ve tekrarlanan değerler yer
tarafında bir şey kazandırıyor mu. Bu sorular şemadan bağımsızdır: aynı yıldız şeması iki farklı
biçimde yazıldığında aynı cevabı çok farklı maliyetlerle verir. Sonraki ders bu biçimi ölçer.

### Sütunlu Dosya Biçimleri

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/sutunlu-dosya-bicimleri

Seçici okumanın ve sıkıştırmanın okunan hücre üzerinden sayılması: iki sütun seçildiğinde sütunlu okuma 400 hücre ve 520 maliyet, satırlı okuma 1400 hücre ve 1520 maliyet veriyor; seçilen sütun sayısı arttıkça fark 200'er hücre kapanıyor ve yedinci sütunda tam olarak sıfırlanıyor, yani sütunlu biçimin kazancı sorgunun okumadığı sütun kadar. Sıkıştırma okunan hücre üzerinden modellendiğinde bu kapanma noktası tablodan çıkıyor: yedi sütunun tamamı sütunlu düzende 851, satırlı düzende 1400 hücre. Kazanç ayrık değer sayısının değil koşu sayısının işi; bölge sütunu beş ayrık değer taşırken 158 koşu veriyor. Dosya içi sıralama tek bir sorgu için seçilir: bölge ile tipe göre sıralamak o sorguyu 253 hücreden 15'e indiriyor ama metreküp sütununu 196'dan 200'e, yani sıkıştırmasız değere geri çıkarıyor.

Önceki üç ders tablonun şeklini tartıştı: olgu ile boyutun ayrılması, normalleştirme
derecesinin birleştirmeye ödettiği, tarihçe tutmayan boyutun cevabı sessizce yanlışlaması.
Üçünde de tablo mantıksal bir nesne olarak durdu — satırları, sütunları ve bir sonucu olan
bir küme. Bir şey hiç sorulmadı: bu tablo diske indiğinde ne yan yana duruyor, satırlar mı
sütunlar mı.

Bu dersin ölçtüğü şey o karardır. Ölçü değişmez: dokunulan dosya, okunan hücre ve maliyet.
Değişen tek şey, aynı sorgunun aynı dosyadan kaç hücre okumak zorunda kaldığıdır. İki soru
sırayla yanıtlanır. Seçici okuma ne kazandırıyor ve bu kazanç nerede biter; sıkıştırma o
sınırı nereye taşır.

- **AM41.** Kaynak, hattan çıkan tüketim satırları ve dosya düzeni **kurgudur** ve ders
  içinde üretilir. Hiçbir ambar, göl, dosya biçimi ya da sorgu motoru çağrılmaz. Tohum
  20260218.
- **AM42.** **Sütunlu dosya biçimi (columnar file format)** ve **satırlı dosya biçimi
  (row-oriented file format)** birer **sınıf adıdır**. Ayrım tek bir davranıştadır: sütunlu
  okuma yalnız seçilen sütunları alır, satırlı okuma kaydın tamamını alır.
- **AM43.** Kayıt yedi sütun taşır. Okunan hücre, dokunulan satır ile okunan sütunun
  çarpımıdır.
- **AM44.** Maliyet, dosya sayısı ile **dosya sabiti 120**'nin çarpımına okunan hücrenin
  eklenmesidir. Sabit bir seçimdir; bu derste iki düzen de aynı tek dosyaya dokunduğu için
  sabit **farkın dışında kalır**.
- **AM45.** Sıkıştırma **okunan hücre üzerinden** modellenir, algoritma yazılmaz. Bir sütunun
  sıkışmış bedeli o sütundaki **koşu (run)** sayısıdır: dosya sırasında ardışık duran aynı
  değerler tek hücreye iner. Sıkıştırma yordamı Linux ve Sistem Yönetimi müfredatındaki
  Linux'a Giriş kursunda ölçüldü; **tekrarlanmaz**.
- **AM46.** Satırlı düzende koşu birimi kaydın tamamıdır. Abone ile dönem çiftleri tekil
  olduğu için ardışık iki kayıt hiçbir zaman eşit değildir ve daralma olmaz.
- **AM47.** Dosya içi sıralama bir **yazma kararıdır** ve dosya başına **tek** olabilir.
- **AM48.** Bölüm anahtarı bu derste sabittir ve `donem`'dir. Anahtarın kendisi sonraki
  dersin konusudur.

#### Seçici Okumanın Ödettiği

Sorgu tek bir dönemin bölge bazlı tüketimini istiyor; ihtiyacı olan iki sütun var, `bolge` ve
`m3`. Aşağıdaki blok hattı, düzeni ve maliyet ölçüsünü kurar, sonra aynı sorguyu seçilen sütun
sayısını birden yediye çıkararak süpürür.

```python
# KURGUDUR. Olcum aginin sayac okumalari , tuketim satirlari ve dosya duzeni ders
# icinde uretilir; hicbir ambar , gol , dosya bicimi ya da sorgu motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
SUTUN = ("abone", "donem", "bolge", "tip", "endeks", "birim", "m3")
DOSYA_SABIT = 120          # bir dosyaya dokunmanin sabit bedeli , hucre cinsinden
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    """Hattan cikan tuketim satirlari. Saglam kaynakta ayikla ve tekille asamalari
    satir dusurmedigi icin tuketim dogrudan kaynaktan , donem farkiyla hesaplanir."""
    ham = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            ham.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                        "endeks": e, "birim": "m3"})
    tablo, cikti = {(k["abone"], k["donem"]): k for k in ham}, []
    for k in ham:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is not None:
            cikti.append(dict(k, m3=k["endeks"] - onceki["endeks"]))
    return cikti


def yaz(kayitlar, bolumleme=()):
    """Satirlari bolumleme anahtarina gore dosyalara dagitir."""
    duzen = {}
    for k in kayitlar:
        duzen.setdefault(tuple(k[a] for a in bolumleme), []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None, sutunlu=True):
    """Dokunulan dosya , okunan hucre ve maliyet = dosya x DOSYA_SABIT + hucre.
    Sutunlu okuma yalniz secilen sutunlari , satirli okuma kaydin tamamini alir."""
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    dosya, hucre = 0, 0
    genislik = len(secilen) if sutunlu else len(SUTUN)
    for anahtar, kayit in duzen.items():
        if budayan:
            es = dict(zip(bolumleme, anahtar))
            if any(es[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * genislik
    return {"dosya": dosya, "hucre": hucre, "maliyet": dosya * DOSYA_SABIT + hucre}


SATIR = satirlar()
DUZEN = yaz(SATIR, ("donem",))
EKLEME = ["bolge", "m3", "abone", "tip", "birim", "endeks", "donem"]
print(f"hattan cikan satir {len(SATIR)} , donem duzeninde dosya {len(DUZEN)} ,"
      f" dosya basina satir {len(SATIR) // len(DUZEN)}")
print("\nsecilen sutun   sutunlu hucre  maliyet   satirli hucre  maliyet   fark")
for n in range(1, len(SUTUN) + 1):
    sec = tuple(EKLEME[:n])
    a = sorgu(DUZEN, ("donem",), sec, {"donem": "2026-07"}, True)
    b = sorgu(DUZEN, ("donem",), sec, {"donem": "2026-07"}, False)
    print(f"{n:>13}   {a['hucre']:>13}  {a['maliyet']:>7}   {b['hucre']:>13}"
          f"  {b['maliyet']:>7}   {b['maliyet'] - a['maliyet']:>4}")
```

```
hattan cikan satir 2200 , donem duzeninde dosya 11 , dosya basina satir 200

secilen sutun   sutunlu hucre  maliyet   satirli hucre  maliyet   fark
            1             200      320            1400     1520   1200
            2             400      520            1400     1520   1000
            3             600      720            1400     1520    800
            4             800      920            1400     1520    600
            5            1000     1120            1400     1520    400
            6            1200     1320            1400     1520    200
            7            1400     1520            1400     1520      0
```

İkinci satır dersin başlangıç sayısıdır. Aynı dosyaya, aynı 200 satıra, aynı süzgeçle dokunan
iki düzenden biri **400 hücre** okuyup **520** ödüyor, öbürü **1400 hücre** okuyup **1520**
ödüyor. Hiçbiri daha akıllı değildir; biri sorgunun istemediği beş sütunu almak zorundadır,
öbürü değildir.

Tablonun asıl bulgusu sağdaki iki sütunun sabitliğidir. Satırlı okuma **1400 hücrede** durur ve
seçilen sütun sayısına hiç tepki vermez, çünkü kaydın tamamını zaten ilk sorguda almıştır.
Sütunlu okuma her eklenen sütunla **tam 200 hücre** büyür. Fark bu yüzden düzenli iner:
1200, 1000, 800, 600, 400, 200 ve yedinci sütunda **0**.

Kapanma noktası bir eşik değil, bir tanımdır. Sütunlu biçimin kazandırdığı şey sorgunun
**okumadığı** sütun sayısıdır, ne eksik ne fazla. Yedinci sütunda kazanç sıfırlanır, çünkü orada
sorgu zaten kaydın tamamını istemektedir. Bir düzenin "sütunlu olduğu için hızlı" diye anılması
bu tablonun okunmadığı anlamına gelir: iki sütun okuyan sorgu için doğru, yedi sütun okuyan
sorgu için yanlıştır.

Dosya sabitinin bu tabloda payı yoktur. İki düzen de tek dosyaya dokunduğu için maliyetin sabit
parçası ikisinde de **120**'dir ve maliyet farkı hücre farkına birebir eşittir. Sabit 60 ya da
480 olsaydı sağdaki sütun değişmezdi. Bu, sabite duyarsız bir sonuçtur ve sonraki derste tersi
görülür.

#### Sıkıştırma Okunan Hücre Üzerinden Ölçülür

Yukarıdaki model her hücreyi bir birim sayar. Sütunlu düzenin ikinci kazancı buradadır: bir
sütunun bütün değerleri **bitişik** durduğu için ardışık tekrarlar tek hücreye inebilir. Aşağıdaki
blok bu daralmayı koşu sayısıyla ölçer ve dosyanın yedi sütununu tek tek sayar.

```python
# Bu blok ilk bloktaki SUTUN , DOSYA_SABIT , satirlar() ve yaz() tanimlarini surdurur.
def kosu(degerler):
    """Ardisik ayni degerlerin olusturdugu blok sayisi."""
    n, onceki = 0, object()
    for d in degerler:
        if d != onceki:
            n += 1
            onceki = d
    return n


def sik_hucre(kayit, secilen, sutunlu=True):
    """Sikismis okuma , hucre cinsinden. Sutunlu duzende her sutun kendi icinde
    bitisiktir ve ardisik ayni deger tek hucreye iner; satirli duzende kayit butun
    olarak durur , iki kaydin anahtari ayni olmadigi icin daralma olmaz."""
    if not sutunlu:
        return len(kayit) * len(SUTUN)
    return sum(kosu([k[s] for k in kayit]) for s in secilen)


DOSYA = yaz(satirlar(), ("donem",))[("2026-07",)]
print(f"tek dosya , donem 2026-07 , satir {len(DOSYA)}")
print("\nsutun     ayrik deger   kosu   sikismamis   sikismis")
for s in SUTUN:
    print(f"{s:<9} {len(set(k[s] for k in DOSYA)):>11}   {kosu([k[s] for k in DOSYA]):>4}"
          f"   {len(DOSYA):>10}   {kosu([k[s] for k in DOSYA]):>8}")
ham, sik = len(DOSYA) * len(SUTUN), sik_hucre(DOSYA, SUTUN)
sat = sik_hucre(DOSYA, SUTUN, False)
print(f"\nyedi sutun , sutunlu sikismamis: hucre {ham} , maliyet {ham + DOSYA_SABIT}")
print(f"yedi sutun , sutunlu sikismis  : hucre {sik} , maliyet {sik + DOSYA_SABIT}")
print(f"yedi sutun , satirli           : hucre {sat} , maliyet {sat + DOSYA_SABIT}")
```

```
tek dosya , donem 2026-07 , satir 200

sutun     ayrik deger   kosu   sikismamis   sikismis
abone             200    200          200        200
donem               1      1          200          1
bolge               5    158          200        158
tip                 2     95          200         95
endeks            200    200          200        200
birim               1      1          200          1
m3                 46    196          200        196

yedi sutun , sutunlu sikismamis: hucre 1400 , maliyet 1520
yedi sutun , sutunlu sikismis  : hucre 851 , maliyet 971
yedi sutun , satirli           : hucre 1400 , maliyet 1520
```

Önceki bölümün kapanma noktası bu blokla tablodan çıkar. Yedi sütunun tamamını okuyan sorgu
sütunlu düzende **851 hücre**, satırlı düzende **1400 hücre** okur; maliyet **971**'e karşı
**1520**'dir. Seçici okumanın kazancı bittiği yerde sıkıştırmanın kazancı sürer ve fark
**549 hücrede** durur.

Koşu sütunu ile ayrık değer sütununun ayrışması dersin en yanıltıcı yeridir. `bolge` **beş**
ayrık değer taşır, buna karşın **158 koşu** verir: dosya abone sırasında yazıldığı için beş
değer iki yüz satıra serpilmiştir ve neredeyse hiç ardışık tekrar yoktur. `donem` ile `birim`
tek değer taşır ve **1 hücreye** iner; ikisi de dosya boyunca değişmez. Sıkışan şey **az sayıda
farklı değer** değil, **yan yana duran aynı değerdir**.

`abone` ile `endeks` sütunları bunun karşı ucudur. İkisi de satır başına tekil olduğu için
**200 koşu** verir ve sıkışmamış değerle birebir aynı kalır. Bu iki sütunu okuyan bir sorgu
sütunlu düzende hiçbir sıkıştırma kazancı görmez.

#### Sıralama Tek Bir Sorgu İçin Seçilir

Koşu sayısı dosyanın yazılış sırasına bağlıdır, dolayısıyla bir yazma kararıyla değiştirilebilir.
Aşağıdaki blok aynı dosyayı üç ayrı sırayla yazıp üç sorgunun okuduğu hücreyi karşılaştırır.

```python
# Bu blok onceki iki bloktaki tanimlari surdurur.
SIRALAMA = {"abone (dogal)": lambda k: (k["abone"],),
            "bolge , tip": lambda k: (k["bolge"], k["tip"]),
            "m3": lambda k: (k["m3"],)}
SORGU = {"bolge+tip": ("bolge", "tip"), "m3": ("m3",), "abone+endeks": ("abone", "endeks")}
DOSYA = yaz(satirlar(), ("donem",))[("2026-07",)]
print("tek dosya , okunan hucre ; parantez icinde maliyet = 1 x 120 + hucre")
print("\nsiralama         " + "".join(f"{a:>16}" for a in SORGU) + "      yedi sutun")
for ad, anahtar in SIRALAMA.items():
    s = sorted(DOSYA, key=anahtar)
    h = [sik_hucre(s, sec) for sec in SORGU.values()] + [sik_hucre(s, SUTUN)]
    print(f"{ad:<15} " + "".join(f"{f'{x} ({x + DOSYA_SABIT})':>16}" for x in h))
h = [len(DOSYA) * len(sec) for sec in SORGU.values()] + [len(DOSYA) * len(SUTUN)]
print(f"{'sikistirmasiz':<15} " + "".join(f"{f'{x} ({x + DOSYA_SABIT})':>16}" for x in h))
```

```
tek dosya , okunan hucre ; parantez icinde maliyet = 1 x 120 + hucre

siralama                bolge+tip              m3    abone+endeks      yedi sutun
abone (dogal)          253 (373)       196 (316)       400 (520)       851 (971)
bolge , tip             15 (135)       200 (320)       400 (520)       617 (737)
m3                     252 (372)        46 (166)       400 (520)       700 (820)
sikistirmasiz          400 (520)       200 (320)       400 (520)     1400 (1520)
```

İkinci satır kazancın en büyüğüdür. Dosya bölgeye ve tipe göre sıralandığında bu iki sütunu okuyan
sorgu **253 hücreden 15 hücreye** iner, maliyet **373**'ten **135**'e düşer. Beş bölge ile iki tip
artık bitişik bloklar hâlinde durmaktadır ve on beş koşu bütün dosyayı anlatmaya yetmektedir.

Aynı satırın ikinci sütunu bu kursun ikinci iddiasını taşır: **düzen de bozar.** Bölgeye göre
sıralamak `m3` sütununu **196 koşudan 200 koşuya** çıkarır. Sayı küçüktür ama yönü nettir — o sütun
için sıralama yalnızca yararsız değil, doğal sıradan **kötüdür** ve sıkıştırmasız değerle
(**200**) birebir aynı yere oturur. Metreküp sütununu okuyan sorgu, bölge sorgusu için alınmış bir
karar yüzünden sıkıştırmanın tamamını kaybeder.

Üçüncü satır aynı takası ters yönden gösterir. Dosya metreküpe göre sıralandığında o sütun
**46 hücreye** iner, buna karşılık bölge ile tip **252**'de kalır, yani doğal sıradaki 253'ten
ayırt edilemez. Bir dosyanın **tek** bir sıralaması olabilir; ikisi aynı anda seçilemez.

Son sütun kararın toplam bedelini verir: yedi sütunun tamamı doğal sırada **851**, bölge sırasında
**617**, metreküp sırasında **700** hücre. Üç sayı da satırlı düzenin **1400**'ünün altındadır,
ama hangisinin seçileceği yalnızca **hangi sorgunun adının yazıldığına** bağlıdır. Sıralaması bir
sorgu adı olmadan seçilmiş bir dosya, kursun kuralı gereği ölçülmemiştir.

#### Özet

- Sütunlu ve satırlı biçim arasındaki fark tek bir davranıştır: sütunlu okuma seçilen sütunları,
  satırlı okuma kaydın tamamını alır. İki sütunluk sorguda sütunlu düzen **400 hücre / 520
  maliyet**, satırlı düzen **1400 hücre / 1520 maliyet** öder.
- Seçici okumanın kazancı sorgunun okumadığı sütun kadardır ve her eklenen sütunla 200 hücre
  erir; **yedinci sütunda tam olarak sıfırlanır**. İki düzen de tek dosyaya dokunduğu için dosya
  sabiti 120 bu farkın dışında kalır.
- Sıkıştırma okunan hücre üzerinden modellendiğinde kapanma noktası tablodan çıkar: yedi sütunun
  tamamı sütunlu düzende **851**, satırlı düzende **1400** hücredir.
- Sıkışan şey az sayıda ayrık değer değil, yan yana duran aynı değerdir: `bolge` beş ayrık değerle
  **158 koşu** verirken `donem` tek değerle **1 koşuya** iner; satır başına tekil olan `abone` ve
  `endeks` hiç sıkışmaz.
- Dosya içi sıralama bir sorgu için seçilir ve başka bir sorgunun kazancını götürür: bölge ile tipe
  göre sıralamak o sorguyu **253'ten 15 hücreye** indirirken `m3` sütununu **196'dan 200'e**, yani
  sıkıştırmasız değerine geri çıkarır.

#### Sonraki Adım

Bu dersin bütün sayıları **tek bir dosyanın içinde** ölçüldü. Bölüm anahtarı `donem` olarak
sabit tutuldu, süzgeç hep o anahtarın üstüne düştü ve her sorgu tek dosyaya dokundu; dosya sabiti
bu yüzden hiçbir farkı değiştirmedi. Anahtarın kendisi hiç sorgulanmadı. Sonraki ders bu sabiti
kaldırır ve iki soruyu sorar: süzgeç bölüm anahtarının **dışında** bir alana düştüğünde ne
oluyor ve aynı veriyi daha çok dosyaya bölmek — okunan hücre hiç artmasa bile — maliyeti nereye
taşıyor.

### Bölümleme ve Dosya Düzeni

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/bolumleme-ve-dosya-duzeni

Bölüm anahtarı seçiminin ve bölüm inceliğinin sorguya ödettiğinin sayılması: dört düzen aynı üç soruya birebir aynı cevabı veriyor (200, 1122 ve 46 satır) ama maliyetleri ayrışıyor. Süzgeç bölüm anahtarının dışına düştüğünde hiçbir dosya budanamıyor ve bölümlemesiz düzenin 4520 maliyeti dönem artı bölge düzeninde 11000'e çıkıyor; farkın tamamı dosya sabitinin elli dört kez fazladan ödenmesidir. Budayan sorguda bile incelik maliyeti 520'den 1000'e ve 1600'e taşıyor, buna karşılık okunan hücre üç düzende de 400'de duruyor: küçük dosya sorunu budur ve maliyetin sabit payı 120'den 600'e ve 1200'e çıkarken hücre payı hiç değişmiyor. Aynı on sorguluk üç ayrı karışımda en ucuz düzen üç ayrı düzen çıkıyor, dolayısıyla anahtar veriye değil sorgu karışımına bakılarak seçiliyor.

Bir önceki dersin bütün sayıları **tek bir dosyanın içinde** ölçüldü. Bölüm anahtarı `donem`
olarak sabit tutuldu, süzgeç hep o anahtarın üstüne düştü ve her sorgu tam olarak bir dosyaya
dokundu. Dosya sabiti bu yüzden hiçbir farkın içine girmedi ve sütunlu okumanın kazancı temiz
göründü. Anahtarın kendisi hiç sorgulanmadı.

Bu ders sabiti kaldırır. İki soru sırayla yanıtlanır: süzgeç bölüm anahtarının **dışında** bir
alana düştüğünde ne oluyor ve aynı veriyi daha çok dosyaya bölmek — okunan hücre hiç artmasa
bile — maliyeti nereye taşıyor. İkinci sorunun yanıtı bu kursun ikinci iddiasının en açık
örneğidir.

- **AM49.** Kaynak, tüketim satırları ve dosya düzeni **kurgudur** ve ders içinde üretilir.
  Hiçbir ambar, göl, dosya biçimi ya da sorgu motoru çağrılmaz. Tohum 20260218.
- **AM50.** **Bölümleme (partitioning)** satırları bir anahtarın değerine göre ayrı **bölümlere**
  dağıtmaktır; bu modelde her bölüm **tek bir dosyadır** ve iki sözcük eşanlamlı kullanılır.
  Bölümleme yordamı Veritabanları müfredatındaki İlişkisel Veritabanı Yönetimi kursunda ölçüldü;
  **tekrarlanmaz**.
- **AM51.** **Parçalama (sharding)** ayrı bir kavramdır ve **karıştırılmaz**: parçalama satırları
  kendi depolaması olan bağımsız düğümlere dağıtır. Sistem Tasarımı ve Dağıtık Sistemler
  müfredatındaki Veri Katmanı Ölçekleme kursunda ölçüldü. Bu derste tek bir depolama vardır;
  ölçülen şey **dosya düzenidir**, düğüm dağılımı değil.
- **AM52.** **Bölüm budama (partition pruning)**: süzgeçteki alan bölüm anahtarındaysa
  eşleşmeyen dosyalara **hiç dokunulmaz**. Anahtar dışındaki alan hiçbir dosyayı budamaz ve
  bütün dosyalar baştan sona okunur.
- **AM53.** Maliyet, dokunulan dosya ile **dosya sabiti 120**'nin çarpımına okunan hücrenin
  eklenmesidir. Sabit bir seçimdir; bu derste ölçülen farkların çoğu **doğrudan onun cinsindendir**
  ve her sonuç sabitin değeriyle birlikte yazılır.
- **AM54.** Sorgu iki sütun okur (`bolge` ve `m3`); okuma sütunludur. Sütun seçiminin etkisi bir
  önceki derste ölçüldü ve burada sabit tutulur.
- **AM55.** Üç soru sabittir: **A** tek dönemi, **B** tek tipi, **C** tek dönemin tek bölgesini
  ister. Üçünün de doğru cevabı düzenden bağımsızdır.

#### Bölümleme Anahtarı ve Budama

Aşağıdaki blok hattan çıkan 2200 satırı dört ayrı düzende yazar ve üç soruyu her düzende sorar.
Dosya sayısının yanında en küçük ve en büyük dosyanın satır sayısı da basılır.

```python
# KURGUDUR. Olcum aginin sayac okumalari , tuketim satirlari ve dosya duzeni ders
# icinde uretilir; hicbir ambar , gol , dosya bicimi ya da sorgu motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
DOSYA_SABIT = 120          # bir dosyaya dokunmanin sabit bedeli , hucre cinsinden
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    """Hattan cikan tuketim satirlari. Saglam kaynakta ayikla ve tekille asamalari
    satir dusurmedigi icin tuketim dogrudan kaynaktan , donem farkiyla hesaplanir."""
    ham = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            ham.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                        "endeks": e, "birim": "m3"})
    tablo, cikti = {(k["abone"], k["donem"]): k for k in ham}, []
    for k in ham:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is not None:
            cikti.append(dict(k, m3=k["endeks"] - onceki["endeks"]))
    return cikti


def yaz(kayitlar, bolumleme=()):
    """Satirlari bolumleme anahtarina gore dosyalara dagitir; her bolum bir dosyadir."""
    duzen = {}
    for k in kayitlar:
        duzen.setdefault(tuple(k[a] for a in bolumleme), []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None):
    """Suzgecin bolumleme anahtarindaki alani dosya budar; anahtar disindaki alan
    hicbir dosyayi budamaz ve dosyanin tamamini okutur."""
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    kalan = {a: v for a, v in suzgec.items() if a not in bolumleme}
    dosya, hucre, satir = 0, 0, 0
    for anahtar, kayit in duzen.items():
        if budayan:
            es = dict(zip(bolumleme, anahtar))
            if any(es[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * len(secilen)
        satir += sum(1 for k in kayit if all(k[a] == v for a, v in kalan.items()))
    return {"dosya": dosya, "hucre": hucre, "satir": satir,
            "maliyet": dosya * DOSYA_SABIT + hucre}


SEC = ("bolge", "m3")
DUZENLER = [("bolumlemesiz", ()), ("donem", ("donem",)),
            ("donem+bolge", ("donem", "bolge")),
            ("donem+bolge+tip", ("donem", "bolge", "tip"))]
SORULAR = [("A donem", {"donem": "2026-07"}),
           ("B tip", {"tip": "isyeri"}),
           ("C donem+bolge", {"donem": "2026-07", "bolge": "kuzey"})]
TUM = satirlar()
print(f"hattan cikan satir {len(TUM)} , secilen sutun {len(SEC)} ,"
      f" dosya sabiti {DOSYA_SABIT} hucre")
print("A: donem suzgeci , B: tip suzgeci , C: donem+bolge suzgeci")
print("\nduzen             dosya  kucuk  buyuk" +
      "".join(f"{a[0] + ' dosya/hucre/mal':>21}" for a, _ in SORULAR))
cevap = {}
for ad, bl in DUZENLER:
    d = yaz(TUM, bl)
    boy = [len(x) for x in d.values()]
    olcum = []
    for sad, f in SORULAR:
        s = sorgu(d, bl, SEC, f)
        cevap.setdefault(sad, set()).add(s["satir"])
        olcum.append(f"{s['dosya']}/{s['hucre']}/{s['maliyet']}")
    print(f"{ad:<17} {len(d):>5}  {min(boy):>5}  {max(boy):>5}" +
          "".join(f"{x:>21}" for x in olcum))
print("\neslesen satir , dort duzende de: " +
      " , ".join(f"{a[0]} {sorted(cevap[a])[0]}" for a, _ in SORULAR))
```

```
hattan cikan satir 2200 , secilen sutun 2 , dosya sabiti 120 hucre
A: donem suzgeci , B: tip suzgeci , C: donem+bolge suzgeci

duzen             dosya  kucuk  buyuk    A dosya/hucre/mal    B dosya/hucre/mal    C dosya/hucre/mal
bolumlemesiz          1   2200   2200          1/4400/4520          1/4400/4520          1/4400/4520
donem                11    200    200            1/400/520         11/4400/5720            1/400/520
donem+bolge          55     31     49           5/400/1000        55/4400/11000             1/92/212
donem+bolge+tip     110     12     28          10/400/1600         55/2244/8844             2/92/332

eslesen satir , dort duzende de: A 200 , B 1122 , C 46
```

Son satır bütün tablonun ön koşuludur: dört düzen üç soruya da **birebir aynı** cevabı veriyor —
200, 1122 ve 46 satır. Ölçülen şey doğruluk değil, aynı cevabın kaça mal olduğudur.

İlk satır tabanı verir. Bölümlemesiz düzende tek bir dosya vardır, her soru onu baştan sona
okur ve maliyet üç soruda da **4520**'dir. Bu düzen soruya **kayıtsızdır**; ne ödüllendirir ne
cezalandırır.

A sorgusu bölümlemenin vaadidir. `donem` düzeninde süzgeç anahtarın üstüne düşer, on dosya hiç
açılmaz ve okunan hücre **4400'den 400'e** iner; maliyet **4520'den 520'ye** düşer. Buraya kadar
bölümleme her ölçüde kazandırır.

B sorgusu vaadin sınırıdır ve bu kursun **ikinci iddiasının** en açık örneğidir. Süzgeç `tip`
alanına düşer, `tip` hiçbir düzenin ilk iki anahtarında yoktur, dolayısıyla **budama olmaz** ve
bütün dosyalar okunur. Okunan hücre üç düzende de **4400**'dür; değişen tek şey dosya sayısıdır:
1, 11 ve 55. Maliyet **4520**, **5720** ve **11000** olur. Aynı hücreyi okuyup aynı 1122 satırı
döndüren iki düzen arasındaki **6480**'lik fark, dosya sabitinin **54 kez fazladan** ödenmesinden
başka bir şey değildir. Bölümleme burada işi kötüleştirmiştir.

Bu satırın başabaş noktası da hesaplanabilir ve şaşırtıcı derecede alçaktır. Budama olmadığında
okunan hücre dosya sayısından bağımsız olarak 4400'dür, dolayısıyla maliyet dosya sayısıyla
doğrusal büyür ve bölümlemesiz düzenin 4520'sine ancak **tek dosyada** eşitlenir. Anahtar dışı bir
süzgeç için **birden çok bölüm her zaman kayıptır**; kaybın büyüklüğü değil varlığı bile
tartışmaya açık değildir.

Dördüncü satır akla gelen ilk düzeltmedir: `tip` de anahtara konur. B sorgusu artık budar, dosya
55'e iner ve hücre **2244**'e düşer. Buna karşılık maliyet **8844**'tür ve hâlâ bölümlemesiz
düzenin neredeyse iki katıdır. Hücrede kazanılan 2156 birim, elli beş dosyanın sabit payı olan
6600 birimi karşılamaz. Bir sorgu için anahtara sütun eklemenin bedeli **110 dosyadır** ve o bedel
bütün öbür sorgulara da yansır.

#### Küçük Dosya Sorunu

İkinci iddianın ikinci örneği budamanın **çalıştığı** yerde görülür. A sorgusu üç bölümlü düzende
de budar; aşağıdaki blok maliyeti iki parçaya ayırır.

```python
# Bu blok ilk bloktaki satirlar() , yaz() , sorgu() , SEC , TUM , DUZENLER ve
# SORULAR tanimlarini surdurur.
print("A sorgusu (budayan suzgec): maliyetin sabit payi ile hucre payi")
print("\nduzen             dokunulan dosya   sabit payi   hucre payi   maliyet")
for ad, bl in DUZENLER:
    s = sorgu(yaz(TUM, bl), bl, SEC, {"donem": "2026-07"})
    print(f"{ad:<17} {s['dosya']:>15}   {s['dosya'] * DOSYA_SABIT:>10}"
          f"   {s['hucre']:>10}   {s['maliyet']:>7}")

KARISIM = [("A agirlikli", (8, 0, 2)), ("C agirlikli", (2, 0, 8)),
           ("B agirlikli", (1, 8, 1))]
print("\non sorguluk karisimin toplam maliyeti")
print("\nkarisim (A,B,C)   " + "".join(f"{a:>18}" for a, _ in DUZENLER) + "        en ucuz")
for kad, agirlik in KARISIM:
    toplam = []
    for ad, bl in DUZENLER:
        d = yaz(TUM, bl)
        toplam.append(sum(n * sorgu(d, bl, SEC, f)["maliyet"]
                          for n, (_, f) in zip(agirlik, SORULAR)))
    print(f"{kad:<17} " + "".join(f"{t:>18}" for t in toplam)
          + f"        {DUZENLER[toplam.index(min(toplam))][0]}")
```

```
A sorgusu (budayan suzgec): maliyetin sabit payi ile hucre payi

duzen             dokunulan dosya   sabit payi   hucre payi   maliyet
bolumlemesiz                    1          120         4400      4520
donem                           1          120          400       520
donem+bolge                     5          600          400      1000
donem+bolge+tip                10         1200          400      1600

on sorguluk karisimin toplam maliyeti

karisim (A,B,C)         bolumlemesiz             donem       donem+bolge   donem+bolge+tip        en ucuz
A agirlikli                    45200              5200              8424             13464        donem
C agirlikli                    45200              5200              3696              5856        donem+bolge
B agirlikli                    45200             46800             89212             72684        bolumlemesiz
```

Üçüncü sütun ilk tablonun okunmayan bulgusudur: **hücre payı üç bölümlü düzende de 400'dür.**
Sorgunun ihtiyaç duyduğu 200 satır ne artar ne azalır; incelen bölümleme onları yalnızca daha çok
dosyaya dağıtır. Okunan veri hiç büyümeden maliyet **520'den 1600'e**, yani üç katına çıkar ve
artışın tamamı sabit payındadır: **120, 600, 1200**. **Küçük dosya sorunu (small file problem)
budur** — bir sorgunun
okuduğu veri değişmezken, o veriye ulaşmak için açılan dosya sayısının büyümesi.

Sonucun sabite bağlılığı burada açık yazılır. Fark tümüyle **dosya sabiti 120** cinsindendir;
sabit 60 olsaydı üç düzenin maliyeti 460, 700 ve 1000, sabit 480 olsaydı 880, 2800 ve 5200 olurdu.
Sıralama değişmez, çünkü sabit pozitif olduğu sürece daha çok dosya daha pahalıdır; değişen
**büyüklüktür**. "İnce bölümleme pahalıdır" cümlesi sabitin değeri yazılmadan ölçülmüş sayılmaz.

Sabitin neyi temsil ettiği burada söylenmelidir. Bir dosyaya dokunmanın satır sayısından bağımsız
bir bedeli vardır: dosyanın yerini bulmak, açmak, başlığını okumak ve hangi sütunun nerede
durduğunu çözmek. Bu iş dosya on iki satır da taşısa iki bin iki yüz satır da taşısa aynıdır.
Modelin yaptığı tek şey o bedeli **hücre cinsinden** yazmaktır; 120 sayısı bir ölçüm değil, ölçüyü
tek birime indirmek için verilmiş bir karşılıktır.

Bölüm sayısı büyüdükçe dosyalar ayrıca **eşitsizleşir**. İlk tablonun küçük ve büyük sütunları
bunu verir: 55 dosyalı düzende dosyalar 31 ile 49 satır arasında, 110 dosyalı düzende 12 ile 28
satır arasındadır. Aboneler bölgelere eşit dağılmadığı için ortalama 40 ve 20 satır hiçbir dosyayı
tarif etmez; sabit bedel ise en küçük dosya için de en büyüğü için de aynıdır. En uçtaki iki dosya
bunu açık eder: 110 dosyalı düzende en küçük dosya iki sütunla **24 hücre** okutur ve o dosyaya
dokunmanın bedelinin **beşte dördünden fazlası** sabittir; bölümlemesiz düzendeki tek dosya 4400
hücre okutur ve sabitin payı yüzde üçün altında kalır. Bölümlemenin ödettiği şey, sabiti gitgide
daha az satıra bölmektir.

#### Anahtar Bir Sorgu Karışımı İçin Seçilir

İkinci tablo on sorguluk üç ayrı karışımın toplam maliyetini verir ve dersin kararını buraya
taşır. A ağırlıklı karışımda en ucuz düzen `donem`, C ağırlıklı karışımda `donem`+`bolge`,
B ağırlıklı karışımda ise **bölümlemesiz** düzendir. Üç karışım, üç kazanan.

Ortadaki satır bir öncekinin tersini söyler. C sorgusu hem döneme hem bölgeye süzgeç uyguladığı
için `donem`+`bolge` düzeninde tek dosyaya iner ve **92 hücre / 212 maliyet** verir; aynı sorgu
`donem` düzeninde 400 hücre okur. Küçük dosya sorunu bu sorguda yoktur, çünkü incelik sorgunun
süzgeciyle **örtüşür**. Aynı 55 dosyalı düzen A sorgusunda ceza, C sorgusunda ödüldür.

Son satır en serttir: yalnızca anahtar dışı süzgeç kullanan bir iş yükünde hiç bölümlememek en
ucuz karardır — 45200'e karşı 46800, 89212 ve 72684. Bölümleme, sorgusu yazılmadığında ödenen ama
karşılığı alınmayan bir bedeldir.

Bu üç satır kursun kuralının tanımıdır. Bir düzenin sayısı sakladığı satır değil, bir sorgunun ona
dokunmak için ödediğidir; **hangi sorgu için seçildiği yazılmayan düzen ölçülmemiş sayılır.** Bir
bölüm anahtarı veriye bakılarak seçilemez, çünkü dört düzen de aynı veriyi saklar ve aynı
cevabı verir. Seçim yalnızca sorgu karışımına bakılarak yapılır ve karışım değiştiğinde karar
yeniden ölçülür.

#### Özet

- Süzgeçteki alan bölüm anahtarındaysa eşleşmeyen dosyalara hiç dokunulmaz: A sorgusu
  bölümlemesiz düzende 4400 hücre okurken `donem` düzeninde 400 okur, maliyet 4520'den 520'ye
  iner.
- Süzgeç anahtarın dışına düştüğünde budama olmaz ve bölümleme işi kötüleştirir: B sorgusunda
  okunan hücre üç düzende de 4400 iken maliyet **4520, 5720 ve 11000**'dir; 6480'lik farkın
  tamamı dosya sabitinin 54 kez fazladan ödenmesidir. Anahtara `tip` eklemek maliyeti 8844'e
  çeker ama 110 dosya karşılığında.
- Budayan sorguda bile incelik ceza yazar: A sorgusunda okunan hücre üç bölümlü düzende de
  **400**'dür, maliyet **520, 1000, 1600** olur ve artışın tamamı sabit payındadır (120, 600,
  1200). Küçük dosya sorunu budur.
- Sonuç dosya sabiti 120 cinsindendir: sabit 60'ta üç değer 460/700/1000, 480'de 880/2800/5200
  olur. Sıralama sabitin işaretine bağlıdır, büyüklüğü değerine.
- Dört düzen üç soruya da aynı cevabı verir (200, 1122, 46 satır); on sorguluk üç karışımda en
  ucuz düzen sırasıyla `donem`, `donem`+`bolge` ve bölümlemesiz çıkar. Anahtar veriye değil sorgu
  karışımına bakılarak seçilir.

#### Sonraki Adım

Bu dersin bütün düzenlerinde on iki dönemin tamamı aynı yerde duruyordu. Hiçbir satır silinmedi,
hiçbir dönem başka bir katmana taşınmadı; en eski dönem en yenisiyle aynı bedelle okunabilir
durumdaydı. Oysa dosya sabiti her dosya için aynıdır ve dosya sayısı dönemle birlikte büyür. Son
ders bu birikimi bir karara çevirir: eski dönemler sıcak katmandan çıkarıldığında sıcak katmanda
kaç dosya ve ne kadar maliyet kalıyor, arşivden okumak ne kadara mal oluyor ve geriye dönük olarak
**hangi soru artık yanıtlanamıyor**.

### Veri Saklama Politikası

Source: https://academia.sh/tr/kurslar/veri-depolama-mimarileri/veri-saklama-politikasi

Arşive taşımanın kazandırdığının ve kapattığının sayılması: altı dönemlik sıcak pencerede sıcak katman 2200 satırdan 1200'e, 55 dosyadan 30'a iner, anahtar dışı süzgeç kullanan sorgu 11000 yerine 6000 öder ve arşivlenen 1000 satır 25 özet girişine indiği için on iki dönemlik bölge toplamı 11000 yerine 6675'e mal olur; arşivlendikçe tam tarihli sorgu ucuzlar. Bedel çözünürlüktedir: bölge kırılımı on bir dönemin tamamında yanıtlanır ve sıcak katman ile arşiv toplamı tam veriyle birebir aynı çıkar, buna karşılık tip, abone ve bölge artı tip kırılımları yalnız altı dönemde yanıtlanır ve sırasıyla 10, 1000 ve 50 kova kapanır. Korumasız bir tip sorgusu hata vermez, işyeri toplamını 29932 yerine 16818 döndürür ve 13114 metreküp görünmez kalır. Ders kursun kapanışıdır.

Bir önceki dersin bütün düzenlerinde on iki dönemin tamamı aynı yerde duruyordu; en eski dönem en
yenisiyle aynı bedelle okunuyordu. Oysa dosya sabiti her dosya için aynıdır ve bölümlü bir düzende
dosya sayısı dönemle birlikte büyür: bir yıl sonra aynı sorgu iki katı dosyaya dokunacaktır.

Bu ders o birikimi bir karara çevirir. Saklama politikası eski dönemleri arşive taşır ve üç şey
ölçülür: sıcak katmanda kalan dosya ile maliyet, arşivden okumanın bedeli ve geriye dönük olarak
**hangi sorunun artık yanıtlanamadığı**. Üçüncüsü asıl bulgudur; ilk ikisi kazançtır ve tek
başlarına kararı hep aynı yöne çevirir.

- **AM56.** Kaynak, tüketim satırları, sıcak katman ve arşiv **kurgudur** ve ders içinde üretilir.
  Hiçbir ambar, göl, bulut servisi ya da sorgu motoru çağrılmaz. Tohum 20260218.
- **AM57.** **Saklama penceresi (retention window)** son N dönemi sıcak katmanda tutar. Sıcak
  katman `donem`+`bolge` bölümlemesiyle yazılır ve sorgu iki sütun okur; ikisi de önceki iki derste
  ölçüldü, burada sabit tutulur.
- **AM58.** **Arşivleme (archiving)** silme değildir: satırlar kalır ama **başka bir çözünürlükte**
  kalır. Arşiv onları tek dosyada `bolge` ile `donem` toplamına indirger, abone ve tip kırılımı
  taşınmaz; dersin bütün kayıp sayıları bu seçime bağlıdır.
- **AM59.** Arşiv girişi üç hücredir: bölge, dönem, toplam. **Arşiv sabiti 600**, sıcak katmanın
  dosya sabitinin beş katıdır; ikisi de seçimdir.
- **AM60.** Saklama ve yaşam süresi yordamı Bulut Bilişim müfredatındaki Bulutta Depolama ve Veri
  kursunda ölçüldü; **tekrarlanmaz**. Silmenin geriye dönük denetime etkisi Veri Mühendisliğine
  Giriş kursundaki Veri Yönetişimi dersinden **girdi** alınır: altı dönemlik pencere altı dönem
  silerken denetlenemez dönemi **1'den 7'ye** çıkarıyordu.

#### Arşive Taşımanın Kazandırdığı

Aşağıdaki blok sıcak pencereyi on iki dönemden bire daraltır; her adımda kalan dosyayı, anahtar
dışı süzgeçli sorgunun maliyetini, arşivin büyüklüğünü ve on iki dönemlik bölge toplamının uçtan
uca maliyetini basar.

```python
# KURGUDUR. Olcum aginin sayac okumalari , tuketim satirlari , sicak katman ve
# arsiv ders icinde uretilir; hicbir ambar , gol , bulut servisi ya da sorgu
# motoru cagrilmaz.
TOHUM, M32 = 20260218, 0xFFFFFFFF


def uretec(t):
    x = ((t ^ (t >> 16)) * 2246822507) & M32
    x = ((x ^ (x >> 13)) * 3266489909) & M32
    s = [(x ^ (x >> 16)) & M32]

    def sonraki():
        s[0] = (s[0] * 1664525 + 1013904223) & M32
        return s[0] / 4294967296
    return sonraki


BOLGE = ["dogu", "bati", "kuzey", "guney", "merkez"]
TIP = ["mesken", "isyeri"]
DONEM = [f"2026-{a:02d}" for a in range(1, 13)]
SICAK_SABIT = 120          # sicak katmanda bir dosyaya dokunmanin sabit bedeli
ARSIV_SABIT = 600          # arsivde bir dosyaya dokunmanin sabit bedeli , bir secim
ABONE = {}
for i in range(200):
    r = uretec(TOHUM + 37 * i)
    ABONE[f"A{i + 1:03d}"] = {"bolge": BOLGE[int(r() * 5)], "tip": TIP[int(r() * 2)],
                              "baslangic": 10000 + int(r() * 60000)}


def satirlar():
    # Hattan cikan tuketim satirlari. Saglam kaynakta ayikla ve tekille satir
    # dusurmedigi icin tuketim dogrudan kaynaktan , donem farkiyla hesaplanir.
    ham = []
    for i, (ab, a) in enumerate(ABONE.items()):
        e = a["baslangic"]
        for d in DONEM:
            r = uretec(TOHUM + 101 * i + 7 * DONEM.index(d))
            e += 4 + int(r() * 46)
            ham.append({"abone": ab, "donem": d, "bolge": a["bolge"], "tip": a["tip"],
                        "endeks": e, "birim": "m3"})
    tablo, cikti = {(k["abone"], k["donem"]): k for k in ham}, []
    for k in ham:
        i = DONEM.index(k["donem"])
        onceki = tablo.get((k["abone"], DONEM[i - 1])) if i else None
        if onceki is not None:
            cikti.append(dict(k, m3=k["endeks"] - onceki["endeks"]))
    return cikti


def yaz(kayitlar, bolumleme=()):
    duzen = {}
    for k in kayitlar:
        duzen.setdefault(tuple(k[a] for a in bolumleme), []).append(k)
    return duzen


def sorgu(duzen, bolumleme, secilen, suzgec=None):
    # Anahtardaki alan dosya budar , anahtar disindaki budamaz.
    suzgec = {} if suzgec is None else suzgec
    budayan = {a: v for a, v in suzgec.items() if a in bolumleme}
    dosya, hucre = 0, 0
    for anahtar, kayit in duzen.items():
        if budayan:
            es = dict(zip(bolumleme, anahtar))
            if any(es[a] != v for a, v in budayan.items()):
                continue
        dosya += 1
        hucre += len(kayit) * len(secilen)
    return {"dosya": dosya, "hucre": hucre, "maliyet": dosya * SICAK_SABIT + hucre}


def arsivle(kayitlar):
    # Satirlar tek dosyada bolge x donem toplamina iner; abone ve tip tasinmaz.
    ozet = {}
    for k in kayitlar:
        ozet[(k["bolge"], k["donem"])] = ozet.get((k["bolge"], k["donem"]), 0) + k["m3"]
    return ozet


BOL = ("donem", "bolge")
SEC = ("bolge", "m3")
TUM = satirlar()
print(f"hattan cikan satir {len(TUM)} , tuketim tasiyan donem"
      f" {len(set(k['donem'] for k in TUM))} , sicak sabit {SICAK_SABIT} ,"
      f" arsiv sabiti {ARSIV_SABIT}")
print("\npencere  sicak satir  sicak dosya   B sicak  arsiv satir  ozet giris"
      "  arsiv mal   12 donem toplami")
for p in (12, 9, 6, 3, 1):
    tut = set(DONEM[len(DONEM) - p:])
    sicak = [k for k in TUM if k["donem"] in tut]
    soguk = [k for k in TUM if k["donem"] not in tut]
    d = yaz(sicak, BOL)
    b = sorgu(d, BOL, SEC, {"tip": "isyeri"})
    ozet = arsivle(soguk)
    # Arsiv tek dosyadir , giris basina uc hucre: bolge , donem , toplam.
    am = ARSIV_SABIT + 3 * len(ozet) if soguk else 0
    print(f"{p:>7}  {len(sicak):>11}  {len(d):>11}  {b['maliyet']:>8}  {len(soguk):>11}"
          f"  {len(ozet):>10}  {am:>9}  {sorgu(d, BOL, SEC)['maliyet'] + am:>18}")
```

```
hattan cikan satir 2200 , tuketim tasiyan donem 11 , sicak sabit 120 , arsiv sabiti 600

pencere  sicak satir  sicak dosya   B sicak  arsiv satir  ozet giris  arsiv mal   12 donem toplami
     12         2200           55     11000            0           0          0               11000
      9         1800           45      9000          400          10        630                9630
      6         1200           30      6000         1000          25        675                6675
      3          600           15      3000         1600          40        720                3720
      1          200            5      1000         2000          50        750                1750
```

İlk satır bir önceki dersin bıraktığı yerdir: sıcak katman 55 dosya taşır, anahtar dışı süzgeçli
sorgu **11000** öder. Aşağı inen her satır bir dönemi çıkarır ve maliyeti tam **1000** birim
düşürür — bir dönem beş dosya ile 400 hücredir.

Altıncı sütun sürprizdir: arşiv büyüdükçe maliyeti **630, 675, 720, 750** olarak neredeyse yerinde
sayar, çünkü arşive giden her dönem 200 satır götürüp yalnızca **5 giriş**, yani 15 hücre bırakır.
Son sütun bu ikisinin toplamıdır ve beklentiyi tersine çevirir: **arşivlendikçe tam tarihli sorgu
ucuzlar** — bölge toplamı arşivsiz **11000**, altı dönem arşivlendiğinde **6675**, bir dönem
dışında her şey arşivlendiğinde **1750** eder. Arşiv yavaş değil, **önceden toplanmış** bir
katmandır.

Karar iki sabite de duyarlıdır, ama ayrı biçimde. Arşiv sabitinin başabaş değeri **985**'tir: bir
dönemi arşivden okumak sabit artı on beş hücre, sıcak katmandan okumak **1000** birimdir. Sıcak
katmanın dosya sabiti ise **yönü değiştirmez** — bir dönemi çıkarmak sorgudan beş dosya ile 400
hücre siler, arşive on beş hücre ekler; sabit 120'de kazanç 985, sabit 0'da bile 385'tir. Yönü
belirleyen indirgemenin varlığı, büyüklüğü belirleyen 120 seçimidir.

#### Arşivin Kapattığı Soru

Bu sütunlar yalnız kazanç gösterir ve tek başlarına pencereyi bire kadar daraltmayı önerir.
Aşağıdaki blok bedeli sayar.

```python
# Bu blok ilk bloktaki satirlar() , yaz() , sorgu() , arsivle() , TUM , BOL ve SEC
# tanimlarini surdurur. Pencere alti donem.
PENCERE = 6
TUT = set(DONEM[len(DONEM) - PENCERE:])
SICAK = [k for k in TUM if k["donem"] in TUT]
OZET = arsivle([k for k in TUM if k["donem"] not in TUT])
SICAK_DONEM = sorted(set(k["donem"] for k in SICAK))
ARSIV_DONEM = sorted(set(d for _, d in OZET))
KIRILIM = {"bolge": ("bolge",), "tip": ("tip",), "abone": ("abone",),
           "bolge+tip": ("bolge", "tip")}


def toplam(kayitlar, alanlar):
    t = {}
    for k in kayitlar:
        ad = tuple(k[a] for a in alanlar)
        t[ad] = t.get(ad, 0) + k["m3"]
    return t


print(f"pencere {PENCERE} donem , sicak donem {len(SICAK_DONEM)} ,"
      f" arsiv donemi {len(ARSIV_DONEM)}")
print("\nkirilim     kova  sicaktan  arsivden  yanitlanan donem  kapali donem  kapali kova")
for ad, alanlar in KIRILIM.items():
    kova = len(toplam(TUM, alanlar))
    arsivden = len(ARSIV_DONEM) if set(alanlar) <= {"bolge"} else 0
    yanit = len(SICAK_DONEM) + arsivden
    kapali = len(SICAK_DONEM) + len(ARSIV_DONEM) - yanit
    print(f"{ad:<11} {kova:>5}  {len(SICAK_DONEM):>8}  {arsivden:>8}  {yanit:>16}"
          f"  {kapali:>12}  {kapali * kova:>11}")

karma = {b: v for (b,), v in toplam(SICAK, ("bolge",)).items()}
for (b, _), v in OZET.items():
    karma[b] = karma.get(b, 0) + v
print(f"\nbolge toplami , tam veri ile sicak+arsiv birebir ayni mi:"
      f" {toplam(TUM, ('bolge',)) == {(b,): v for b, v in karma.items()}}")
tam, sic = toplam(TUM, ("tip",))[("isyeri",)], toplam(SICAK, ("tip",))[("isyeri",)]
print(f"isyeri toplami , on bir donem {tam} ; yalniz sicak katmandan {sic} ;"
      f" gorunmeyen {tam - sic} m3")
```

```
pencere 6 donem , sicak donem 6 , arsiv donemi 5

kirilim     kova  sicaktan  arsivden  yanitlanan donem  kapali donem  kapali kova
bolge           5         6         5                11             0            0
tip             2         6         0                 6             5           10
abone         200         6         0                 6             5         1000
bolge+tip      10         6         0                 6             5           50

bolge toplami , tam veri ile sicak+arsiv birebir ayni mi: True
isyeri toplami , on bir donem 29932 ; yalniz sicak katmandan 16818 ; gorunmeyen 13114 m3
```

İlk satır arşivin doğru yaptığı şeydir: bölge kırılımı **on bir dönemin tamamında** yanıtlanır ve
sıcak katmanla arşivin toplamı tam veriyle **birebir aynıdır**. Arşiv yaklaşıklık değil, kendi
çözünürlüğünde kesindir.

Kalan üç satır kaybı verir. `tip`, `abone` ve `bolge`+`tip` yalnız **altı dönemde** yanıtlanır,
beş dönem için **hiç** yanıtlanamaz; kapalı kova sırasıyla **10, 1000 ve 50**'dir. Maliyet bu
satırlarda tanımsızdır, çünkü cevap hiçbir bedele okunamaz: bir düzen bazı soruları pahalı değil
**imkânsız** kılar.

Saklamanın iki biçimi burada ayrılır. Silme **dönem** kaybettirir: önceki kurs altı dönemlik
pencerenin denetlenemez dönemi 1'den 7'ye çıkardığını ölçtü. Arşivleme **sütun** kaybettirir:
hiçbir dönem yok olmaz, beş dönem üç kırılımından ikisini bırakır. Biri zaman, öbürü kırılım
eksenini keser.

Son satır dersin en tehlikeli sayısıdır. Tip kırılımı arşivde yoktur ama sıcak katmanda vardır;
korumasız bir sorgu hata vermez, elinin altındakini toplar ve işyeri toplamını **29932 yerine
16818** döndürür. **13114 metreküp** uyarısız görünmez kalır: sorgu yanlış yazılmamış, veri
bozulmamış, hiçbir denetim ateşlememiştir. Yanıt **daha küçük bir soruya** aittir; iki sorunun
sözcükleri aynıdır.

#### Özet

- Pencere bir dönem daraldığında anahtar dışı süzgeçli sorgu tam **1000** birim ucuzlar: 11000'den
  altı dönemlik pencerede **6000**'e iner.
- Arşiv önceden toplanmış olduğu için ucuzdur: her dönem 200 satır götürüp **5 giriş** bırakır, on
  iki dönemlik bölge toplamı **11000'den 6675'e** iner, başabaş arşiv sabiti **985**'tir.
- Kayıp çözünürlüktedir: bölge kırılımı tam veriyle birebir aynı kalır ama `tip`, `abone` ve
  `bolge`+`tip` beş dönem için hiç yanıtlanamaz (kapanan kova **10, 1000, 50**); silme dönem
  kaybettirir, arşivleme sütun.
- Korumasız bir tip sorgusu hata vermeden eksik yanıt verir: işyeri toplamı **29932 yerine 16818**
  çıkar.

#### Kurs Kapanışı

Satırlar ortak birim taşımaz; her satırın **hangi ölçütle** okunduğu ders adının yanındadır.
Parantezdeki fark düzenli kurulum eksi düzensiz tabandır: **eksi ucuzlama, artı pahalılaşma**.

| Ders | Düzensiz taban | Düzenli kurulum | Bedel |
|---|---|---|---|
| `depolama-yaklasimlari/01` — dönem sorgusunun maliyeti | bölümlemesiz ve satırlı, 15400 hücre, **15520** | `donem` bölümlü ve sütunlu, 400 hücre, **520** (**−15000**) | anahtar dışı süzgeçte 5720 (**+1200**); şema kapısı 400 satırı atar, ölçümleri sağlamdır |
| `depolama-yaklasimlari/02` — dönem sorgusunun maliyeti | ham göl, 14400 hücre, **14520** | ambar, 400 hücre, **520** (**−14000**) | göl yazmada 1000 hücre kazanır, tek sorguda 14000 geri öder; iki tüketici 48004 ve 58454 m3 bulur |
| `depolama-yaklasimlari/03` — yakalanan bozulma sınıfı ve saklanan hücre | dayatmasız düzenek **0/6**, gecikme tanımsız, 14400 hücre | okumada dayatan düzenek **6/6**, ortalama 1,33 aşama (**+6**) | saklanan hücre **29800**; alan adı değişiminde 1800 yerine 2200 satır, `birim_degisti` hiçbir kapıya takılmaz |
| `depolama-yaklasimlari/04` — üç soruluk kümenin maliyeti | bölümlemesiz ambar **13.560** | iki pazar **2.580** (**−10.980**) | 3.322 fazladan satır, satır başına 2,51 kopya; ayrışan tazelemede 126 çift, aynı soruya 4.707 ile 5.340 |
| `depolama-yaklasimlari/05` — karar veren taraf / dokunulan düzen, altı soruluk karışım | merkezî **1/1**, **3.120** | veri örgüsü **1/5**, **2.000** (**−1.120**) | veri ağı 5/5 ve **6.280** (**+3.160**); ölçülmemiş anahtar tek başına 2.450 |
| `depolama-yaklasimlari/06` — beş aramada bakılan giriş | katalogsuz **295** | katalogla **29** (**−266**) | bayat katalogda 406'ya karşı 251; iki arama katalogsuzdan pahalı biter (129/122, 104/100) |
| `analitik-modelleme/01` — sorgu maliyeti ve saklanan hücre | düz tablo, 15400 hücre, **4520** | olgu ve iki boyut, 7236 hücre, **5040** (**+520**) | birleştirme 0'dan **2200**'e, sonuç aynı; bölge adı düzeltmesi 506 yerine 46 satır |
| `analitik-modelleme/02` — sorgu maliyeti ve birleştirme | yıldız: 2200 birleştirme, 212 boyut satırı, **5040** | kar tanesi: 4400, 217, **5170** (**+130**) | beş satır için birleştirme ikiye katlanır, sonuç aynı; tek nitelikte 10 hücre kaybı, altıda 965 kazanç |
| `analitik-modelleme/03` — işyeri toplamı (m3) ve maliyet | üzerine yazan boyut **35893**, 200 satır, **7240** | tarihçe tutan boyut **33145**, 2200 satır, **13440** (**+6200**) | **2748 m3 yanlış kovada**, kovaların toplamı 58454'te aynı, dört denetim yeşil |
| `analitik-modelleme/04` — iki sütunluk sorgunun okuduğu hücre | satırlı, 1400 hücre, **1520** | sütunlu, 400 hücre, **520** (**−1000**) | fark yedinci sütunda **0**; sıkıştırmayla yedi sütun 851'e karşı 1400; bölge sıralaması `m3` koşusunu 196'dan 200'e çıkarır |
| `analitik-modelleme/05` — anahtar dışı süzgecin maliyeti | bölümlemesiz **4520** | `donem`+`bolge` **11000** (**+6480**) | farkın tamamı dosya sabitinin 54 kez fazladan ödenmesi; budayan sorguda hücre 400 sabitken maliyet 520, 1000, 1600 |
| `analitik-modelleme/06` — on iki dönemlik bölge toplamı | arşivsiz, 55 dosya, **11000** | altı dönemlik pencere, 30 dosya, **6675** (**−4325**) | üç kırılım beş dönem için kapanır (10 / 1000 / 50 kova); korumasız sorgu 29932 yerine **16818** |

Tablo tek bir kuralın on iki kez yazılmasıdır. **Bir depolama düzeninin sayısı sakladığı satır
değil, bir sorgunun ona dokunmak için ödediğidir; hangi sorgu için seçildiği yazılmayan düzen
ölçülmemiş sayılır.** İlk sütundaki ölçüt adı kuralın kendisidir: aynı 2200 satır on iki kez
saklandı ve on iki ayrı sayı verdi, hiçbiri verinin büyüklüğünden gelmedi.

**İkinci iddia aynı tablodan okunur: düzen de bozar.** Artı işaretli satırlar bunlardır.
`analitik-modelleme/05` en açığıdır: anahtar dışı süzgeçte **+6480** ve farkın tamamı dosya
sabitidir; aynı derste budayan sorguda bile hücre 400'de dururken maliyet üç katına çıkar.
`depolama-yaklasimlari/01` aynı şeyi küçük ölçekte (**+1200**), `/05` dağıtık kurulumda
(**+3.160**) yazar, `/03`'te altı sınıfı yakalamak saklanan hücreyi 14400'den 29800'e çıkarır.
`analitik-modelleme/01` ve `/02` ayrımın ve normalleştirmenin okumaya yazdığı **+520** ile
**+130**'u taşır, `/04`'te bir sorgu için seçilen sıralama başka bir sütunu sıkıştırmasız değerine
geri iter. `depolama-yaklasimlari/04`'te pazarın bölge kazancı sıfır, `/06`'da bayat katalog iki
aramayı katalogsuzdan pahalı bitirir.

**Üçüncü iddia bir satırda adıyla durur: düzen cevabı sessizce yanlışlar.**
`analitik-modelleme/03`'te tarihçe tutmayan boyut sorguyu yavaşlatmaz, işyeri toplamını **33145
yerine 35893** yapar; **2748 metreküp** yanlış kovaya gider, kovaların toplamı 58454'te aynı kalır,
dört denetim yeşildir. İki yankısı vardır: `/06`'da korumasız sorgu 29932 yerine 16818 döndürür,
`depolama-yaklasimlari/04`'te aynı soru iki adreste 4.707 ve 5.340 verir. Üçünde de hata yoktur,
yalnızca cevap vardır.

On iki derste verinin nerede ve hangi düzende durduğu ölçüldü. Bu düzeni **kimin, ne zaman ve
hangi sırayla** doldurduğu hiç sorulmadı; dosyalar hep yazılmış, boyutlar hep hazır, arşiv hep
taşınmış kabul edildi. Bir adım başarısız olduğunda geri kalanın ne yapacağı, aynı adımın ikinci
kez koşturulmasının satırları çoğaltıp çoğaltmadığı ve bir hattın **kendini yeniden
çalıştırabilmesinin** bedeli sayılmadı. Sonraki kurs düzeni değil, düzeni dolduran sırayı ölçüm
nesnesine dönüştürür.

