Ders 11 / 14
İkincil Dizinler ve Akışlar
Önceki dersin karşılanamayan üç erişim örüntüsünün ikincil dizinle kurtarılması ve karşılığında alınanın sayılması: her dizinin kurtardığı istek, eklediği yazma bedeli, saklama ve dizin gecikmesinden doğan eksik sonuç payı, yansıtma seçiminin taban tabloya dönüşle takası, değişim akışının bir tüketiciyle modellenip olay sırası, yeniden işlenen olay ve akışın saklama süresi dolduğunda geri getirilemeyen kaçırılan pencerenin ölçülmesi.
İçindekiler
Önceki ders sayac_id bölüm anahtarını en dengeli aday olarak buldu ve aynı satırda bir borç
bıraktı: altı erişim örüntüsünün üçü — bölge günlük toplamı, eşik taraması, son bir saatin
okumaları — anahtarı sabitleyemediği için hiç karşılanamıyordu; isteklerin yüzde 30’u. Anahtarı
değiştirmek 518,4 bin kurgu birimle kapalı bir kapı olduğuna göre geriye tek yol kalıyor: aynı
veriyi başka bir anahtara göre bir kez daha yerleştirmek.
Buna ikincil dizin denir; dizinin kurulması, güncellenmesi ve bölümlenmesi devredilmiş kararlardır. Devredilmeyen kısım seçimin kendisidir: hangi anahtar, hangi öznitelikler, kaç dizin.
Düzenek
YV19 — taban tablo önceki dersteki sayac_id | zaman anahtarıyla kuruludur; karşılanamayan üç
örüntü E2 yüzde 17, E4 yüzde 8, E6 yüzde 5 ağırlığındadır. YV20 — üç ikincil dizin adayı:
bolge | gun#sayac_id, gun | okuma_degeri ve gun#saat | sayac_id; ikincisi seyrek dizindir ve
yalnız eşiği aşan öğeleri taşır (öğelerin binde 4’ü). YV21 — dizin eşzamansız
güncellenir, gecikme 0,8 saniyedir. YV22 — yansıtma seçenekleri öğe başına 48, 96 ve 180
bayttır; kurgu saklama fiyatı 0,9 birim/GB-ay, yazma birimi okumanın 5 katı ve istek başına fiyat
önceki dersteki gibi 0,25 birim/1.000 istektir. YV23 — değişim akışı 16 parçadır, saklama
süresi 24 saattir; olay hızı yazma hızına eşittir (44,4 olay/sn) ve tüketici kapasitesi 55
olay/sn’dir. YV24 — tüketici konumunu C saniyede bir kaydeder ve günde ortalama 0,4 çökme
yaşanır. YV23’teki kapasite payı belirleyicidir: 55 yerine 45 yazılırsa yetişme süresi sütunu
patlar.
// yv/model.mjs — ikincil dizin ve degisim akisi MODELI. Onceki dersin taban tablosu (bolum // anahtari sayac_id) uzerine kurulur; oradaki karsilanamayan uc orunttu girdi alinir. Rastgelelik // olay uretecindedir, uretec kendi yazilmistir ve tohum gorunurdur. export const OGE = 345_600_000, GUNLUK_YAZMA = 3_840_000, GUNLUK_ISTEK = 103_700_000; export const YAZMA_KAT = 5, FIYAT_ISTEK = 0.25, FIYAT_GB_AY = 0.9, OGE_BAYT = 180; export const LAMBDA = GUNLUK_YAZMA / 86400; // olay/sn = yazma/sn // [ad, kurtardigi orunttu, orunttunun agirligi, dizinlenen oge orani, o araliga giren yazma/sn] export const DIZIN = [ ["bolge | gun#sayac_id", "E2 bolge gunluk toplami", 17, 1, LAMBDA * 0.27], ["gun | okuma_degeri", "E4 esik asan okumalar", 8, 0.004, LAMBDA * 0.004], ["gun#saat | sayac_id", "E6 son bir saat", 5, 1, LAMBDA], ]; export const YANSITMA = [["yalniz anahtar", 48], ["secili oznitelik", 96], ["tum oznitelikler", 180]]; export const yazmaBedeliAy = (oran) => GUNLUK_YAZMA * oran * 30 * YAZMA_KAT / 1000 * FIYAT_ISTEK; export const saklamaBedeliAy = (oran, bayt) => OGE * oran * bayt / 1e9 * FIYAT_GB_AY; export const eksikOge = (yazmaHizi, L) => yazmaHizi * L; export const eksikSorguPayi = (yazmaHizi, L) => 1 - Math.exp(-yazmaHizi * L); // Degisim akisi: her yazma bir olaydir. Olaylarin SIRASI yalnizca ayni bolum anahtari icinde // korunur; tuketici parcalari sirayla okudugunda genel zaman sirasi bozulur. export function akisiOku({ adet = 60000, parca = 16, tur = 64, tohum = 20260804 } = {}) { let s = tohum % 2147483647; const rast = () => (s = (s * 48271) % 2147483647) / 2147483647; const karma = (x) => { let h = 2166136261; for (const c of String(x)) h = Math.imul(h ^ c.charCodeAt(0), 16777619); return (h >>> 0) % parca; }; const kutu = Array.from({ length: parca }, () => []); let t = 0; for (let i = 0; i < adet; i += 1) { // olaylar zaman sirasinda uretilir t += rast() * 2 / LAMBDA; const sayac = Math.floor(rast() * 40000); kutu[karma(sayac)].push({ t, sayac }); } const okunan = []; // tuketici: her parcadan `tur` olay for (let d = 0; okunan.length < adet; d += 1) for (let p = 0; p < parca; p += 1) for (const o of kutu[p].slice(d * tur, (d + 1) * tur)) okunan.push(o); let ters = 0, enBuyukKayma = 0, enYuksek = -Infinity; const sonAnahtar = new Map(); let anahtarIciTers = 0; for (const o of okunan) { if (o.t < enYuksek) { ters += 1; enBuyukKayma = Math.max(enBuyukKayma, enYuksek - o.t); } enYuksek = Math.max(enYuksek, o.t); if ((sonAnahtar.get(o.sayac) ?? -Infinity) > o.t) anahtarIciTers += 1; sonAnahtar.set(o.sayac, o.t); } return { adet, parca, ters, enBuyukKayma, anahtarIciTers, sure: t }; } // Duraklama: tuketici P saniye durur, sonra K olay/sn ile yetisir. Akisin saklama suresi R'dir; // P > R olan her saniyenin olaylari akistan duser ve BIR DAHA GELMEZ. export const K = 55, R = 24 * 3600, COKME = 0.4; export function duraklama(P) { const birikim = LAMBDA * P; const yetisme = K > LAMBDA ? birikim / (K - LAMBDA) : Infinity; return { birikim, yetisme, kacirilan: LAMBDA * Math.max(0, P - R), toplam: P + yetisme }; } export const yenidenIslenen = (C) => LAMBDA * C * COKME;
// yv/olc.mjs — ikincil dizinin kurtardigi sorgu ve bedeli; degisim akisinin tuketici tarafi import { DIZIN, YANSITMA, yazmaBedeliAy, saklamaBedeliAy, eksikOge, eksikSorguPayi, akisiOku, duraklama, yenidenIslenen, OGE, OGE_BAYT, GUNLUK_YAZMA, GUNLUK_ISTEK, LAMBDA, K, R, COKME } from "./model.mjs"; const s = (x, n) => String(x).padStart(n); const L = 0.8; console.log(`taban tablo: ${(OGE / 1e6).toFixed(1)}M oge, ${(GUNLUK_YAZMA / 1e6).toFixed(2)}M yazma/gun, ` + `${(GUNLUK_ISTEK / 1e6).toFixed(1)}M istek/gun. Karsilanamayan orunttu: E2 %17, E4 %8, E6 %5 (toplam %30).`); console.log(`dizin gecikmesi ${L} sn, olay hizi ${LAMBDA.toFixed(1)}/sn.\n`); console.log("ikincil dizin | kurtardigi istek/gun | dizinlenen oge | ek yazma (birim/ay) | ek saklama (birim/ay) | eksik oge | eksik sonuc"); console.log("---------------------|----------------------|----------------|---------------------|-----------------------|-----------|------------"); let kurtarilanT = 0, yazmaT = 0, saklamaT = 0; for (const [ad, , agirlik, oran, hiz] of DIZIN) { const kurtarilan = GUNLUK_ISTEK * agirlik / 100; const yazma = yazmaBedeliAy(oran), saklama = saklamaBedeliAy(oran, 180); kurtarilanT += kurtarilan; yazmaT += yazma; saklamaT += saklama; console.log(`${ad.padEnd(20)} | ${s((kurtarilan / 1e6).toFixed(1) + "M (%" + agirlik + ")", 20)} | ` + `${s((OGE * oran / 1e6).toFixed(1) + "M", 14)} | ${s(yazma.toFixed(0), 19)} | ${s(saklama.toFixed(1), 21)} | ` + `${s(eksikOge(hiz, L).toFixed(1), 9)} | ${s("%" + (100 * eksikSorguPayi(hiz, L)).toFixed(1), 11)}`); } console.log(`toplam: gunde ${(kurtarilanT / 1e6).toFixed(1)}M istek kurtarildi (karsilanamayan %30 -> %0); ` + `bedel ayda ${(yazmaT + saklamaT).toFixed(0)} kurgu birim.`); console.log(`ek yazma ek saklamanin ${(yazmaT / saklamaT).toFixed(0)} katidir: ikincil dizinin ` + `bedeli sakladigi bayt degil, her yazmanin ikinci kez yazilmasidir.`); console.log("\nyansitma secimi (yalniz E2 dizini icin, 345.6M oge):"); console.log("yansitma | oge basina bayt | dizin boyutu | saklama (birim/ay) | taban tabloya donus"); console.log("------------------|-----------------|--------------|--------------------|--------------------"); for (const [ad, bayt] of YANSITMA) console.log(`${ad.padEnd(17)} | ${s(bayt, 15)} | ${s((OGE * bayt / 1e9).toFixed(1) + " GB", 12)} | ` + `${s(saklamaBedeliAy(1, bayt).toFixed(0), 18)} | ${s(bayt === 180 ? "gerekmez" : "her oge icin 1 okuma", 19)}`); const a = akisiOku({ tur: 24 }); console.log(`\ndegisim akisi: ${a.adet} olay, ${a.parca} parca, ${a.sure.toFixed(0)} sn'lik pencere. Tohum 20260804.`); console.log(`genel zaman sirasinda ters gorunen olay: ${a.ters} (%${(100 * a.ters / a.adet).toFixed(1)}), ` + `en buyuk zaman kaymasi ${a.enBuyukKayma.toFixed(2)} sn.`); console.log(`ayni bolum anahtari icinde ters gorunen olay: ${a.anahtarIciTers}. Garanti burada, genel sirada degil.`); console.log(`yeniden isleme (konum kaydetme araligi C, gunde ${COKME} cokme):`); console.log("C | 1 sn | 5 sn | 15 sn | 60 sn | 300 sn"); console.log("olay | " + [1, 5, 15, 60, 300].map((C) => s(yenidenIslenen(C).toFixed(0), C < 10 ? 4 : C < 100 ? 5 : 6)).join(" | ")); console.log(`\nduraklama: tuketici kapasitesi ${K} olay/sn, olay hizi ${LAMBDA.toFixed(1)}/sn ` + `-> yetisme carpani ${(K / (K - LAMBDA)).toFixed(2)}. Akis saklama suresi ${R / 3600} sa.`); console.log("duraklama | birikmis olay | yetisme suresi | kesintiden tam yetismeye | kacirilan olay"); console.log("----------|---------------|----------------|--------------------------|---------------"); for (const P of [900, 3600, 4 * 3600, 12 * 3600, 24 * 3600, 30 * 3600]) { const d = duraklama(P); console.log(`${s((P / 3600).toFixed(2) + " sa", 9)} | ${s((d.birikim / 1e3).toFixed(0) + "K", 13)} | ` + `${s((d.yetisme / 3600).toFixed(2) + " sa", 14)} | ${s((d.toplam / 3600).toFixed(2) + " sa", 24)} | ` + `${s(d.kacirilan === 0 ? "0" : (d.kacirilan / 1e3).toFixed(0) + "K", 14)}`); } console.log(`kosumdan bagimsiz: kacirilan olay yalniz duraklama saklama suresini astiginda dogar ` + `(${R / 3600} sa) ve geri getirilemez; yetisme suresi bunu degistirmez, yalniz uzatir.`); // Aktarim bedeli: tuketici baska bolgedeyse her olay bolge sinirindan gecer (olay = ogenin onceki // ve sonraki goruntusu, yani oge boyutunun iki kati). Kurgu birim. const gbAy = GUNLUK_YAZMA * 2 * OGE_BAYT * 30 / 1e9; console.log(`\naktarim: ${gbAy.toFixed(1)} GB/ay olay trafigi. Tuketici ayni bolgede 0, baska ` + `bolgede ${(gbAy * 2).toFixed(0)}, bolge disinda ${(gbAy * 9).toFixed(0)} kurgu birim/ay.`);
taban tablo: 345.6M oge, 3.84M yazma/gun, 103.7M istek/gun. Karsilanamayan orunttu: E2 %17, E4 %8, E6 %5 (toplam %30). dizin gecikmesi 0.8 sn, olay hizi 44.4/sn. ikincil dizin | kurtardigi istek/gun | dizinlenen oge | ek yazma (birim/ay) | ek saklama (birim/ay) | eksik oge | eksik sonuc ---------------------|----------------------|----------------|---------------------|-----------------------|-----------|------------ bolge | gun#sayac_id | 17.6M (%17) | 345.6M | 144000 | 56.0 | 9.6 | %100.0 gun | okuma_degeri | 8.3M (%8) | 1.4M | 576 | 0.2 | 0.1 | %13.3 gun#saat | sayac_id | 5.2M (%5) | 345.6M | 144000 | 56.0 | 35.6 | %100.0 toplam: gunde 31.1M istek kurtarildi (karsilanamayan %30 -> %0); bedel ayda 288688 kurgu birim. ek yazma ek saklamanin 2572 katidir: ikincil dizinin bedeli sakladigi bayt degil, her yazmanin ikinci kez yazilmasidir. yansitma secimi (yalniz E2 dizini icin, 345.6M oge): yansitma | oge basina bayt | dizin boyutu | saklama (birim/ay) | taban tabloya donus ------------------|-----------------|--------------|--------------------|-------------------- yalniz anahtar | 48 | 16.6 GB | 15 | her oge icin 1 okuma secili oznitelik | 96 | 33.2 GB | 30 | her oge icin 1 okuma tum oznitelikler | 180 | 62.2 GB | 56 | gerekmez degisim akisi: 60000 olay, 16 parca, 1349 sn'lik pencere. Tohum 20260804. genel zaman sirasinda ters gorunen olay: 56199 (%93.7), en buyuk zaman kaymasi 94.73 sn. ayni bolum anahtari icinde ters gorunen olay: 0. Garanti burada, genel sirada degil. yeniden isleme (konum kaydetme araligi C, gunde 0.4 cokme): C | 1 sn | 5 sn | 15 sn | 60 sn | 300 sn olay | 18 | 89 | 267 | 1067 | 5333 duraklama: tuketici kapasitesi 55 olay/sn, olay hizi 44.4/sn -> yetisme carpani 5.21. Akis saklama suresi 24 sa. duraklama | birikmis olay | yetisme suresi | kesintiden tam yetismeye | kacirilan olay ----------|---------------|----------------|--------------------------|--------------- 0.25 sa | 40K | 1.05 sa | 1.30 sa | 0 1.00 sa | 160K | 4.21 sa | 5.21 sa | 0 4.00 sa | 640K | 16.84 sa | 20.84 sa | 0 12.00 sa | 1920K | 50.53 sa | 62.53 sa | 0 24.00 sa | 3840K | 101.05 sa | 125.05 sa | 0 30.00 sa | 4800K | 126.32 sa | 156.32 sa | 960K kosumdan bagimsiz: kacirilan olay yalniz duraklama saklama suresini astiginda dogar (24 sa) ve geri getirilemez; yetisme suresi bunu degistirmez, yalniz uzatir. aktarim: 41.5 GB/ay olay trafigi. Tuketici ayni bolgede 0, baska bolgede 83, bolge disinda 373 kurgu birim/ay.
Dizin Sorguyu Kurtarır, Bedeli Yazmadan Alır
Üç dizin karşılanamayan üç örüntünün üçünü de kurtarıyor: günde 31,1M istek geri geliyor, karşılanamayan pay yüzde 30’dan sıfıra iniyor. Fatura tek sütunda toplanıyor.
Ek yazma ek saklamanın 2.572 katıdır. Tam dizin ayda 144.000 kurgu birim ek yazma getirirken aynı dizinin sakladığı 62,2 GB yalnız 56 birim tutuyor. Sebep, dizinin bir kopya olmaktan çok bir ikinci yazma yolu olmasıdır: taban tabloya giden her öğe dizine de yazılır. İki tam dizin ekleyen tasarım, veri katmanının yazma bedelini üçe katlar.
Seyrek dizin bu kuralın istisnasıdır. gun | okuma_degeri yalnız eşiği aşan öğeleri, yani
binde dördünü taşıyor ve aynı istekleri ayda 576 birime kurtarıyor: tam dizinin 250’de biri.
Dizinin bedelini belirleyen şey kaç sorguyu kurtardığı değil, kaç yazmaya dokunduğudur.
Son iki sütun eşzamansız güncellemenin sonucunu sayıyor. Dizin 0,8 saniye geride olduğu için
herhangi bir anda bolge dizininde 9,6, saat dizininde 35,6 öğe eksiktir; bölge toplamı ve son
bir saat sorguları neredeyse her koşumda eksik döner (yüzde 100), ama eksik olan pay küçüktür.
Asıl uyarı orta satırdadır: eşik taramalarının yüzde 13,3’ü, o an eşiği aşmış bir okumayı
görmüyor — sekiz taramadan biri taze alarmı kaçırıyor.
Yansıtma tablosu ikinci takası veriyor: yalnız anahtar taşıyan dizin 16,6 GB ve ayda 15 birim, tüm öznitelikleri taşıyan 62,2 GB ve 56 birim. Aradaki 41 birim, dizinden dönen her öğe için taban tabloya ek okuma yapmamanın karşılığıdır ve o okumalar 17,6M sorgu/gün ölçeğinde çok daha pahalıdır.
Değişim Akışının Üç Sayısı
Aynı verinin ikinci bir kullanıcısı var: her yazmayı bir olay olarak dışarı veren değişim akışı. Faturalama özetlerini üreten tüketici bu akıştan beslenir ve üç şeyi bilmek zorundadır.
Sıra garantisi bölüm anahtarı içindedir, akışın tamamında değil. 60.000 olayın 56.199’u, yani yüzde 93,7’si, tüketiciye genel zaman sırasında ters görünüyor ve en büyük zaman kayması 94,73 saniye. Aynı sayacın olaylarında ters çift sayısı sıfır: garanti oradadır. İki farklı sayacın olayını zaman sırasında geldi sanan bir tüketici yanılır.
Teslim en az bir kezdir, tam bir kez değil. Tüketici çöktüğünde son kaydedilen konumdan başlar; konum aralığı 1 saniyeyken günde 18, 300 saniyeyken 5.333 olay ikinci kez işlenir. Bu sayı bir hata değil, tüketicinin etkisiz-tekrarlı olması gereken aralıktır: aynı olayın ikinci işlenişi faturaya ikinci kalem eklememelidir.
Kaçırılan pencere geri getirilemez. Son tablo tüketiciyi P saniye durduruyor. 12 saatlik bir duraklamada 1,92M olay birikiyor ve tüketicinin yetişmesi 50,53 saat sürüyor — kesintiden tam yetişmeye 62,53 saat. Buna karşın kaçırılan olay sıfırdır, çünkü tüketici her zaman en eskiyi önce okur ve hiçbir olay duraklama süresinden fazla beklemez. Sınır tek bir yerdedir: duraklama akışın saklama süresini aştığı anda. 30 saatlik duraklamada 960K olay akıştan düşer ve bir daha gelmez. Bu, kursun geri alınamayan kalemler listesine yeni bir satır ekler — ve ilk kez geri alınamayan şey bir karar değil, bir zaman aralığıdır.
Aktarım satırı kısa: 41,5 GB/ay olay trafiği aynı bölgede ücretsiz, başka bölgede 83, bölge dışında 373 kurgu birim — yazma hızıyla ölçeklenen bir kalem.
Özet
- Üç ikincil dizin karşılanamayan üç örüntünün üçünü de kurtarıyor (günde 31,1M istek, %30’dan %0’a) ve bedeli saklama değil yazmadır: ek yazma ek saklamanın 2.572 katı (144.000’e karşı 56 kurgu birim/ay); seyrek dizin aynı sorguları 576 birime kurtarıyor.
- Eşzamansız dizin 0,8 saniye geridedir: bölge dizininde 9,6, saat dizininde 35,6 öğe eksik ve eşik taramalarının %13,3’ü o an eşiği aşmış bir okumayı görmüyor.
- Yansıtma saklama ile okuma arasında takastır: yalnız anahtar 16,6 GB ve 15 birim, tüm öznitelikler 62,2 GB ve 56 birim — ama ilki her sonuç öğesi için taban tabloya bir okuma ekler.
- Değişim akışında sıra yalnız bölüm anahtarı içinde korunur: genel sırada olayların %93,7’si ters görünüyor (en büyük kayma 94,73 sn), anahtar içi ters çift sıfır.
- Kaçırılan pencere tek geri alınamayan kalemdir: duraklama saklama süresini aşana kadar kayıp yok, aştığında (30 sa) 960K olay düşüyor ve geri gelmiyor.
Sonraki Adım
Bu dört ders veri katmanının iki ucunu ölçtü ve ikisinde de aynı şey görüldü: kararın devri ucuz, geri alınması pahalı. Ama her istek diske gitti. Oysa ölçüm ağının en sık sorulan şeyi çok dar bir kümedir — son okuma değeri, sayaç kütüğü, açık alarm listesi — ve bunları her seferinde taban tablodan okumak aynı yanıtı gün boyunca binlerce kez üretmek demektir. Sonraki ders bu tekrarı bellek içi bir katmana taşıyan yönetilen önbellek servisini ele alır ve yalnız hizmet olmasının getirdiğini sayar: hangi karar devrediliyor, ölçekleme birimi ne, düğüm kaybedildiğinde açılan veri kaybı penceresi ne kadar.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.