İçeriğe geç
academia.sh

Ders 09 / 10

Veri Soyağacı

Bozulmuş bir sunum değerinden kaynağa gitmek için bakılan satır sayısının soyağacı kaydı varken ve yokken sayılması: beş aşamalı hat altı katmanda 9.460 satır taşırken soyağacı kaydı 11.455 bağ tutuyor, çünkü her tüketim satırının iki atası var; kayıt yokken bir sunum değerini açıklamak 9.260 satır okumayı gerektiriyor ve bu sayı hangi değerden başlandığına bağlı değil, kayıtla aynı arama 94 ile 155 satır arasında bitiyor. İki koşum iki ayrı kayıp aşaması veriyor: geç gelen kayıtta kayıp kaynakta, şema değiştiğinde ayıklama aşamasında. Sunum sapmasının büyüklüğü bozulmanın büyüklüğünü vermiyor: beş kat fazla satır bozulduğunda sunum daha az oynuyor ve iki değer yukarı gidiyor.

İçindekiler

Önceki üç ders denetimi satırın kendisine baktırdı. Kalite boyutları satırın taşıdığı değeri ölçtü, şema sözleşmesi satırın alanlarını, yeniden işleme aynı satırın ikinci kez geçmesini. Üçünde de denetimin baktığı nesne elindeki satırdı. Bir soru hiç sorulmadı: bu satır nereden geldi. Sunumda beş değer duruyor; bir tanesi beklenenden uzaklaştığında o değeri üreten satırlar hangileridir ve onları bulmak için kaç satır okunur.

Bu dersin ölçtüğü şey bir arama maliyetidir. Bozulmuş bir sunum değerinden kaynağa gitmek iki biçimde yapılabilir: hattın her katmanını baştan okuyarak ya da hattın koşarken yazdığı soyağacı (lineage) kaydını geriye izleyerek. İkisi de aynı yanıtı bulur; bakılan satır sayısı arasında iki mertebe fark vardır. Kaydın kendisinin de bir bedeli vardır ve o da sayılır.

  • VK41. Kaynak, beş aşamalı hat ve bozulma sınıfları kurgudur ve ders içinde tanımlanır. Hiçbir depo, dosya biçimi, katalog ürünü ya da orkestratör çağrılmaz. Tohum 20260218.
  • VK42. Soyağacı kaydı, her aşamanın kendi çıktı anahtarından o çıktıyı üreten girdi anahtarlarına yazdığı bağ kümesidir. Satır anahtarı abone ile dönemdir, özet anahtarı bölge ile dönemdir, sunum anahtarı bölgedir.
  • VK43. Arama iki biçimde ölçülür. Kayıt yokken sunum değerini açıklamak için altı katmanın tamamı baştan okunur. Kayıt varken her katmanda yalnız kayıtlı atalar okunur. Sayılan birim bakılan satırdır; süre yazılmaz.
  • VK44. Özet hücresinde sapma bandı 0,02 alınmıştır. Özet katmanı 55 hücredir, yani hücre oranlarının en küçük adımı 0,0182; band bu adımın hemen üstündedir. Sunum üzerinde oran basılmaz, değerin kendisi ve farkı yazılır.
  • VK45. Gözlemlenebilirlik, iz ve günlükleme yordamı Backend Geliştirme müfredatındaki Gözlemlenebilirlik ve Güvenilirlik kursunda ve DevOps ve Platform Mühendisliği müfredatındaki Gözlemlenebilirlik ve İşletim kursunda ölçüldü; burada tekrarlanmaz. İzin nesnesi zaman içinde ilerleyen bir istektir; soyağacının nesnesi bir satırın dayandığı satırlardır.
  • VK46. Soyağacı kaydı olmuş koşumdan çıkar. Olması gerekeni bilmez; beklenen ata kümesi sağlam koşumun kaydından gelir.

Soyağacı Kaydı ve Kaç Bağ Tuttuğu

Hattın beş aşaması zaten satır düşürdüğünü sayıyor. Soyağacı bundan fazlasını ister: her çıktı satırının hangi girdi satırlarından doğduğunu ada ada yazmasını. Kayıt aşama işlevlerinin içine değil, girdileriyle çıktıları arasına konur; hat aynı hattır.

# KURGUDUR. Olcum aginin sayac okumalari, bes asamali hat ve bozulma siniflari
# ders icinde tanimlanir; hicbir depo, dosya bicimi ya da orkestrator 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 boz(kayit, sinif, donem="2026-07"):
    """Bozulma siniflari. Hicbiri saldiri degil, uretici tarafi kazasidir."""
    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 == "sema_degisti":
            y = dict(k)
            y["endeks_degeri"] = y.pop("endeks")    # alan adi degisti
            sayac += 1
            yeni.append(y)
        else:
            yeni.append(k)
    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):
    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):
    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


KATMAN = ["kaynak", "ayikla", "tekille", "tuketim", "ozet", "sun"]


def ad(k):
    return (k["abone"], k["donem"])


def soyagaci(kayit):
    """Hatti kosturur ve her asamada cikti anahtarindan girdi anahtarlarina bag yazar."""
    v1, _ = a1_ayikla(kayit)
    v2, _ = a2_tekille(v1)
    v3, _ = a3_tuketim(v2)
    oz, _ = a4_ozet(v3)
    sun, _ = a5_sun(oz)
    bag = {"ayikla": {ad(k): [ad(k)] for k in v1},
           "tekille": {}, "tuketim": {}, "ozet": {}, "sun": {}}
    for k in v1:
        bag["tekille"].setdefault(ad(k), []).append(ad(k))
    for k in v3:
        i = DONEM.index(k["donem"])
        bag["tuketim"][ad(k)] = [ad(k), (k["abone"], DONEM[i - 1])]
        bag["ozet"].setdefault((k["bolge"], k["donem"]), []).append(ad(k))
    for b, d in oz:
        bag["sun"].setdefault(b, []).append((b, d))
    satir = {"kaynak": len(kayit), "ayikla": len(v1), "tekille": len(v2),
             "tuketim": len(v3), "ozet": len(oz), "sun": len(sun)}
    return sun, oz, bag, satir


kaynak = kaynak_uret()
sunum, ozet, bag, satir = soyagaci(kaynak)
print("katman   ", "  ".join(f"{a:>8}" for a in KATMAN))
print("satir    ", "  ".join(f"{satir[a]:>8}" for a in KATMAN))
print("bag      ", "  ".join(f"{sum(len(v) for v in bag[a].values()) if a in bag else 0:>8}"
                             for a in KATMAN))
toplam_bag = sum(sum(len(v) for v in bag[a].values()) for a in bag)
print(f"\nsatir toplami {sum(satir.values())} , bag toplami {toplam_bag}")
print("\nsunum degeri   deger   ata kaynak satiri")
for b, v in sunum.items():
    ata = {x for c in bag["sun"][b] for k in bag["ozet"][c]
           for t in bag["tuketim"][k] for a2 in bag["tekille"][t] for x in bag["ayikla"][a2]}
    print(f"{b:<13} {v:>7}   {len(ata):>17}")
katman      kaynak    ayikla   tekille   tuketim      ozet       sun
satir         2400      2400      2400      2200        55         5
bag              0      2400      2400      4400      2200        55

satir toplami 9460 , bag toplami 11455

sunum degeri   deger   ata kaynak satiri
merkez        1321.45                 588
kuzey         1212.55                 552
dogu           881.36                 408
bati           833.09                 372
guney         1065.55                 480

Kayıt, hattın taşıdığı veriden büyüktür. Altı katman toplam 9.460 satır tutuyor, soyağacı ise 11.455 bağ; satır başına 1,21 bağ. Fazlalığın tamamı tek aşamadan geliyor: tuketim 2.200 satır üretiyor ama 4.400 bağ yazıyor, çünkü bir tüketim satırı iki okumanın farkıdır ve iki atası vardır. Bir aşamanın kaç bağ yazacağını satır sayısı değil, çıktının kaç girdiye dayandığı belirler.

Son tablo soyağacının en doğrudan okumasıdır: sunulan beş değerin her biri kaç kaynak satırının üstünde duruyor. Merkez 588, kuzey 552, güney 480, doğu 408, batı 372; toplamları 2.400, yani kaynağın tamamı. Sunum katmanında beş sayı görünüyor; onların altında iki bin dört yüz satır var ve hangi satırın hangi sayıya gittiği ancak yazıldıysa bilinir.

Bozulmuş Değerden Kaynağa: İki Koşum

Arama şöyle işler. Şüpheli sunum değerinin kayıtlı ataları okunur; sağlam koşumun karşılık gelen hücrelerinden sapan ya da hiç bulunmayan özet hücreleri seçilir; yalnız onların ataları bir alt katmanda okunur. Her katmanda okunan satır sayısı ayrı sayılır. Kayıt yoksa böyle bir daraltma yoktur; sunum değerini açıklamak için altı katmanın tamamı okunur.

# Onceki blogun tanimlari surer: soyagaci, boz, sunum, ozet, bag, satir.
def geri_yuru(hedef, bag_s, ozet_s, kaynak_s, pay=0.02):
    """Sunum degerinden kaynaga: her katmanda yalniz kayitli atalar okunur."""
    bakilan, kayip = {"sun": 1}, None
    aday = bag["sun"][hedef]
    bakilan["ozet"] = len(aday)
    supheli = [c for c in aday if c not in bag_s["ozet"]
               or abs(ozet_s[c] - ozet[c]) / max(abs(ozet[c]), 1) > pay]
    for ust, alt in (("ozet", "tuketim"), ("tuketim", "tekille"), ("tekille", "ayikla")):
        aday = sorted({x for c in supheli for x in bag[ust][c]})
        bakilan[alt] = len(aday)
        yok = [x for x in aday if x not in bag_s[alt]]
        supheli, kayip = (yok, alt) if yok else (aday, kayip)
    aday = sorted({x for c in supheli for x in bag["ayikla"][c]})
    bakilan["kaynak"] = len(aday)
    hic = [x for x in aday if x not in kaynak_s]
    return bakilan, supheli, ("kaynak" if hic else kayip)


for sinif in ("gec_gelen", "sema_degisti"):
    bozuk, n = boz(kaynak, sinif)
    sunum_s, ozet_s, bag_s, satir_s = soyagaci(bozuk)
    kaynak_s = {ad(k) for k in bozuk}
    kayitsiz = sum(satir_s.values())
    print(f"== {sinif} , bozulan kaynak satiri {n} , kayit yokken bakilan {kayitsiz} satir")
    print("sunum      deger     fark    sun ozet tuketim tekille ayikla kaynak"
          "  toplam  kat  koken  kayip asamasi")
    for b in BOLGE:
        bak, sup, asama = geri_yuru(b, bag_s, ozet_s, kaynak_s)
        t = sum(bak.values())
        print(f"{b:<9} {sunum_s[b]:>8.2f} {sunum_s[b] - sunum[b]:>8.2f}"
              f" {bak['sun']:>6} {bak['ozet']:>4} {bak['tuketim']:>7} {bak['tekille']:>7}"
              f" {bak['ayikla']:>6} {bak['kaynak']:>6} {t:>7} {kayitsiz / t:>4.0f}"
              f" {len(sup):>6}  {asama}")
    print()
== gec_gelen , bozulan kaynak satiri 40 , kayit yokken bakilan 9260 satir
sunum      deger     fark    sun ozet tuketim tekille ayikla kaynak  toplam  kat  koken  kayip asamasi
dogu        837.18   -44.18      1   11      68      27      9      9     125   74      9  kaynak
bati        811.18   -21.91      1   11      62      12      4      4      94   99      4  kaynak
kuzey      1185.09   -27.46      1   11      92      21      7      7     139   67      7  kaynak
guney      1011.27   -54.28      1   11      80      33     11     11     147   63     11  kaynak
merkez     1284.00   -37.45      1   11      98      27      9      9     155   60      9  kaynak

== sema_degisti , bozulan kaynak satiri 200 , kayit yokken bakilan 8650 satir
sunum      deger     fark    sun ozet tuketim tekille ayikla kaynak  toplam  kat  koken  kayip asamasi
dogu        878.11    -3.25      1   11      68     102     34     34     250   35     34  ayikla
bati        845.89    12.80      1   11      62      93     31     31     229   38     31  ayikla
kuzey      1218.67     6.12      1   11      92     138     46     46     334   26     46  ayikla
guney      1072.56     7.01      1   11      80     120     40     40     292   30     40  ayikla
merkez     1318.56    -2.89      1   11      98     147     49     49     355   24     49  ayikla

Kayıt yokken bakılan satır sayısı 9.260’tır ve bu sayı beş sunum değerinin hangisinden başlandığına bağlı değildir. Aynı şey ikinci koşumda da geçerlidir: orada da tek bir sayı vardır, 8.650. Kayıtsız aramanın maliyeti hedefe göre değişmez, çünkü daraltacak bir şey yoktur; hattın o koşumda tuttuğu her satır okunur. Soyağacıyla aynı arama ilk koşumda 94 ile 155 satır arasında bitiyor, ikincisinde 229 ile 355 arasında. Güney değeri için oran 63 kat, batı için 99 kat.

Bağ sayısının nasıl daraldığı sütunlarda görülüyor. Özet katmanında her zaman 11 hücre okunuyor; on iki dönem var ama tüketim ilk dönemi düşürdüğü için bölge başına on bir özet hücresi kalıyor. Tüketim katmanında okunan satır bölgenin abone sayısının iki katıdır: merkez 98, doğu 68. Bunlar sapan iki özet hücresinin, yani 2026-07 ile 2026-08’in ataları. Bozulma tek dönemi bozuyor ama tüketim bir farktır ve bozulan dönem iki dönemin farkına girer.

İki koşumun asıl ayrıldığı yer son sütundur. gec_gelen koşumunda kayıp aşaması kaynak: şüpheli anahtarlar hattın hiçbir katmanında yok, kaynak kaydında da yok, yani o satırlar hiç gelmedi. sema_degisti koşumunda kayıp aşaması ayikla: aynı anahtarlar kaynakta duruyor, ama ayıklama aşamasından sonra bağı yok. Kayıt bu iki durumu birbirinden ayırır. Kayıtsız arama ikisinde de aynı 9.260 ya da 8.650 satırı okur ve sonunda yalnız “eksik” der.

Sunum sapmasının büyüklüğü bozulmanın büyüklüğünü vermiyor. sema_degisti beş kat fazla satır bozuyor (200 satır, 40 yerine) ama sunumu daha az oynatıyor: en büyük fark 12,80, ilk koşumda 54,28 idi. Dahası iki değer yukarı gidiyor. Bunun nedeni ikinci koşumda bozulan satırların dönem ortalamasından tamamen çıkması, ilk koşumda ise ortalamaya eksik bir toplamla girmesidir. Sunuma bakarak bozulmanın çapını okumak iki koşumda da yanlış sonuç verir.

Soyağacının Söylemediği

Kayıt bir arama alanı daraltır; bir yanıt vermez. Bunun en açık kanıtı gec_gelen koşumunun kaynak sütunudur. Orada dokuz, on bir, dört anahtar okunuyor ve hepsi bulunamıyor; ama bu anahtarların listesi bozuk koşumun kaydından çıkmadı, çünkü orada onlara ait hiçbir bağ yok. Liste sağlam koşumun kaydından geldi. Soyağacı olmuş olanı yazar; olmamış bir satırı ancak beklenen ata kümesiyle karşılaştırma görünür kılar. Beklenen küme yoksa kayıt yalnız var olanı gösterir ve eksik satır kayıtta da sessizdir.

Kaydın ikinci sınırı bedelidir. 11.455 bağ her koşumda yeniden yazılır; yeniden işlenen bir dönem için hat ikinci kez koştuğunda kayıt da ikinci kez yazılır. Bir önceki derste yeniden işlemenin satırı çoğalttığı ölçülmüştü; soyağacı bu çoğalmayı da taşır ve hangi koşumun bağı olduğu ayrıca yazılmazsa iki koşumun bağları birbirine karışır.

Üçüncü sınır bu dersin dışına çıkar. Kayıp aşaması kaynak çıktığında hat kendi içinde temizdir: hiçbir aşama satır düşürmedi, satır hiç gelmedi. Soyağacı bunu doğru söyler ve orada durur. Kırk satırın neden gelmediğini, kimin göndermesi gerektiğini ve bulgunun kime iletileceğini bilmez. Kaynak, hattın sahibi olmadığı bir yerdir.

Özet

  • Soyağacı kaydı, her aşamanın çıktı anahtarından girdi anahtarlarına yazdığı bağ kümesidir; hat altı katmanda 9.460 satır taşırken kayıt 11.455 bağ tutar, satır başına 1,21.
  • Bağ sayısını satır sayısı değil çıktının kaç girdiye dayandığı belirler: tuketim 2.200 satır üretir, 4.400 bağ yazar, çünkü bir tüketim satırı iki okumanın farkıdır.
  • Kayıt yokken bir sunum değerini açıklamak 9.260 satır okumak demektir ve bu sayı hangi değerden başlandığına bağlı değildir; kayıtla aynı arama 94 ile 155 satır arasında biter.
  • İki koşum iki ayrı kayıp aşaması verir: gec_gelen için kaynak, sema_degisti için ayıklama. Kayıtsız arama ikisini birbirinden ayıramaz.
  • Sunum sapmasının büyüklüğü bozulmanın büyüklüğünü vermez: beş kat fazla satır bozulduğunda en büyük fark 54,28’den 12,80’e düşer ve iki değer yukarı gider.
  • Soyağacı hiç gelmemiş satıra bağ yazamaz; eksik satırı ancak sağlam koşumun beklenen ata kümesiyle karşılaştırma görünür kılar.

Sonraki Adım

Bu derste arama kaynağa kadar indi ve orada durdu. Kayıp aşaması kaynak çıktığında yanıtlanan soru “hangi satırlar” oldu; yanıtlanmayan soru “kim” idi. Hattın beş aşaması var, kaynak da sayılırsa altı; bunların kaçı bir sahip taşıyor, sahibi olmayan bir aşamada yakalanan bozulmanın bulgusu kime gidiyor ve o kişi düzeltebileceği aşamadan kaç adım uzakta duruyor. Aynı soru geriye doğru da sorulur: soyağacı bir dönemin satırlarına bağ yazmış olsa bile, o satırlar saklama süresi dolduğu için silinmişse bağ nereye işaret eder. Sonraki ders sahipliği, erişimi ve saklamayı üç sayıya çevirir ve kursu kapatır.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat