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:
tuketim2.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_geleniçin kaynak,sema_degistiiç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.