Ders 02 / 14
Depolama Sınıfları
Erişim sıklığına göre katmanların ölçülmesi: saklama ücretiyle erişim ücretinin ters yönde çalışması, başabaş erişim sıklığının nesne boyutuna göre taranması, erişim geçmişi üzerinde nesnelerin sınıflara dağıtılması ve yanlış sınıfın iki yönde de sayılması — seyrek veriyi sık sınıfta, sık veriyi arşivde tutmanın yıllık bedeli.
İçindekiler
Önceki ders kurgu kovadaki dört aileyi tek bir saklama oranıyla faturalandırdı. Oysa aileler birbirine benzemiyor: sayaç okumaları küçük ve çok sayıda, fatura belgeleri büyük ve seyrek okunan, saha fotoğrafları bir kez yazılıp neredeyse hiç açılmayan kayıtlar. Nesne deposu bu farkı depolama sınıfı (storage class) ile fiyat tarafına geçirir: aynı kovadaki nesneler farklı sınıflarda tutulabilir ve sınıf nesne başına seçilir.
Sınıf bir kalite kadranı değildir. Dayanıklılık dört sınıfta da aynıdır; değişen tek şey ücretin saklama ile erişim arasında nasıl bölündüğüdür. Bu yüzden “seyrek erişilen sınıf daha ucuzdur” cümlesi karar taşımaz; karar taşıyan sayı başabaş erişim sıklığıdır.
- DT7 — Dört sınıf ve ücretleri kurgudur; sınıflar yalnız erişim sıklığıyla anılır, ücretler kurgu birim cinsindendir.
- DT8 — Erişim geçmişi karışım modelidir: sıcak pay ile sıcak ve soğuk yıllık ortalamalar modelde yazılıdır, üreteç modelin içindedir, tohum 40127’dir.
- DT9 — Sınıf nesne başına seçilir; yıllık okuma sayısı yıl boyunca sabittir.
// sinif/sinif-modeli.mjs — depolama siniflari ve erisim gecmisi MODELDIR. Ucretler kurgu birimdir, // siniflar erisim sikligiyla anilir; hicbir ticari ad kullanilmaz. export const SINIF = [ // saklama: B/GiB/ay, istek ve gecis: B/istek, getirme: B/GiB { ad: "sik erisilen", saklama: 0.0230, istek: 0.00040 / 1000, getirme: 0.000, enAz: 0, gecis: 0.0050 / 1000 }, { ad: "seyrek erisilen", saklama: 0.0125, istek: 0.00100 / 1000, getirme: 0.010, enAz: 30, gecis: 0.0100 / 1000 }, { ad: "cok seyrek erisilen", saklama: 0.0040, istek: 0.00250 / 1000, getirme: 0.020, enAz: 90, gecis: 0.0250 / 1000 }, { ad: "arsiv", saklama: 0.0010, istek: 0.00500 / 1000, getirme: 0.030, enAz: 180, gecis: 0.0500 / 1000 }, ]; export const CIKIS_INTERNET = 0.085; // B/GiB, kurgu // Erisim gecmisi karisim modelidir: her ailenin bir sicak payi var, gerisi soguk. export const AILE = [ // adet, ortalama boyut, sicak pay, sicak ve soguk yillik okuma ortalamasi { ad: "sayac-okumalari", adet: 1_240_000, ort: 2400, sicak: 0.06, sOrt: 60, cOrt: 0.4 }, { ad: "fatura-belgeleri", adet: 86_000, ort: 380 * 1024, sicak: 0.15, sOrt: 26, cOrt: 0.8 }, { ad: "saha-fotograflari", adet: 42_000, ort: Math.round(3.6 * 1024 * 1024), sicak: 0.03, sOrt: 40, cOrt: 0.15 }, { ad: "is-emri-kayitlari", adet: 310_000, ort: 11 * 1024, sicak: 0.20, sOrt: 34, cOrt: 1.2 }, ]; export const GIB = (bayt) => bayt / 1024 ** 3; // Yillik ucret: saklama + okuma istekleri + geri getirilen bayt. Sinif degistirmek ayri ucrettir. export const yillik = (s, bayt, okumaYil) => 12 * s.saklama * GIB(bayt) + okumaYil * (s.istek + GIB(bayt) * s.getirme); export const TOHUM = 40127; // ureteci kendimiz yaziyoruz, tohum gorunur: kurgudur export function* erisim(aile) { // her nesne icin yillik okuma sayisi (tam sayi) let x = (TOHUM ^ aile.adet) >>> 0; const s = () => (x = (Math.imul(x, 1664525) + 1013904223) >>> 0) / 4294967296; for (let i = 0; i < aile.adet; i++) { const ort = s() < aile.sicak ? aile.sOrt : aile.cOrt; yield Math.floor(-ort * Math.log(1 - s())); } }
Başabaş Erişim Sıklığı
// sinif/basabas.mjs — saklama ile erisim ucretleri ters yonde: basabas erisim sikligi taranir import { SINIF, AILE, GIB, yillik } from "./sinif-modeli.mjs"; const g = (x, n, d = 2) => x.toFixed(d).padStart(n); console.log("sinif | saklama | istek (1000'de) | getirme B/GiB | en az saklama"); console.log("---------------------|---------|-----------------|---------------|--------------"); for (const s of SINIF) console.log(`${s.ad.padEnd(20)} | ${g(s.saklama, 7, 4)} | ${g(s.istek * 1000, 15, 5)} | ` + `${g(s.getirme, 13, 3)} | ${String(s.enAz + " gun").padStart(13)}`); console.log("Saklama asagi inerken istek ve getirme yukari cikiyor: cevrilecek tek yonlu kadran yok."); // Basabas taramasi: yilda 0,1 okuma adimiyla, ucuz saklamali sinif nerede pahaliya donuyor. const basabas = (ucuz, pahali, bayt) => { for (let k = 0; k <= 2000; k++) { const r = k / 10; if (yillik(ucuz, bayt, r) > yillik(pahali, bayt, r)) return r; } return null; }; console.log("\nbasabas erisim sikligi (yilda okuma); ustunde ucuz saklamali sinif pahaliya doner:"); console.log("aile | ort. boyut | seyrek>sik | cok seyrek>seyrek | arsiv>cok seyrek"); console.log("-------------------|------------|------------|-------------------|-----------------"); for (const f of AILE) { const c = [basabas(SINIF[1], SINIF[0], f.ort), basabas(SINIF[2], SINIF[1], f.ort), basabas(SINIF[3], SINIF[2], f.ort)]; const boyut = f.ort < 1024 ** 2 ? (f.ort / 1024).toFixed(1) + " KiB" : (f.ort / 1024 ** 2).toFixed(1) + " MiB"; console.log(`${f.ad.padEnd(18)} | ${boyut.padStart(10)} | ` + c.map((x, i) => x.toFixed(1).padStart([10, 17, 16][i])).join(" | ")); } console.log("Kucuk nesnede basabas asagi iniyor: istek ucreti bayt ucretini bastiriyor ve"); console.log("ucuz saklama yalnizca cok dar bir siklik araliginda kazandiriyor."); // Ayni aile, ayni bayt, iki farkli siklik: siralamanin tersine dondugu nokta. const f = AILE[1], y = (r) => SINIF.map((s) => yillik(s, f.ort, r) * f.adet); const [t0, t12] = [y(0), y(12)]; console.log(`\n${f.ad} (${GIB(f.adet * f.ort).toFixed(1)} GiB) yillik toplam, kurgu birim:`); console.log(` sifir okuma : sik ${g(t0[0], 6)} | arsiv ${g(t0[3], 6)} -> arsiv ${(t0[0] / t0[3]).toFixed(0)} kat ucuz`); console.log(` yilda 12 okuma: sik ${g(t12[0], 6)} | arsiv ${g(t12[3], 6)} -> siralama tersine dondu`);
sinif | saklama | istek (1000'de) | getirme B/GiB | en az saklama ---------------------|---------|-----------------|---------------|-------------- sik erisilen | 0.0230 | 0.00040 | 0.000 | 0 gun seyrek erisilen | 0.0125 | 0.00100 | 0.010 | 30 gun cok seyrek erisilen | 0.0040 | 0.00250 | 0.020 | 90 gun arsiv | 0.0010 | 0.00500 | 0.030 | 180 gun Saklama asagi inerken istek ve getirme yukari cikiyor: cevrilecek tek yonlu kadran yok. basabas erisim sikligi (yilda okuma); ustunde ucuz saklamali sinif pahaliya doner: aile | ort. boyut | seyrek>sik | cok seyrek>seyrek | arsiv>cok seyrek -------------------|------------|------------|-------------------|----------------- sayac-okumalari | 2.3 KiB | 0.5 | 0.2 | 0.1 fatura-belgeleri | 380.0 KiB | 10.9 | 7.3 | 2.2 saha-fotograflari | 3.6 MiB | 12.4 | 9.8 | 3.4 is-emri-kayitlari | 11.0 KiB | 1.9 | 0.7 | 0.2 Kucuk nesnede basabas asagi iniyor: istek ucreti bayt ucretini bastiriyor ve ucuz saklama yalnizca cok dar bir siklik araliginda kazandiriyor. fatura-belgeleri (31.2 GiB) yillik toplam, kurgu birim: sifir okuma : sik 8.60 | arsiv 0.37 -> arsiv 23 kat ucuz yilda 12 okuma: sik 9.01 | arsiv 16.75 -> siralama tersine dondu
Başabaş tablosu sınıf seçiminin nesne boyutuna bağlı olduğunu gösteriyor. 380 KiB’lik fatura belgesi yılda 10,9 okumanın altında kalıyorsa seyrek sınıf kazandırır; 2,3 KiB’lik sayaç okuması için aynı eşik 0,5’tir — yılda bir kez okunan bir kaydı ucuz saklamalı sınıfa koymak zarardır. Nedeni sabit istek ücretidir: küçük nesnede erişimin bedeli baytla değil istek sayısıyla ölçülür. Son iki satır aynı şeyi bir aile üzerinde gösteriyor.
Erişim Geçmişi Üzerinde Dağıtım
- DT10 — Sınıf değiştirmek bir geçiş isteğidir, ayrı ücretlidir ve en az saklama süresi geçişte başlar.
- DT11 — Getirme ücreti çıkış ücretinden ayrıdır; ikisi aynı bayt için birlikte ödenir.
- DT12 — Yılda 24’ten çok okunan nesne modelde “sıcak” sayılır.
// sinif/dagitim.mjs — erisim gecmisi uzerinde siniflar dagitilir; yanlis sinif iki yonde sayilir import { SINIF, AILE, CIKIS_INTERNET, GIB, yillik, TOHUM, erisim } from "./sinif-modeli.mjs"; const g = (x, n, d = 2) => x.toFixed(d).padStart(n); const enUcuz = (bayt, r) => { const t = SINIF.map((s) => yillik(s, bayt, r)); return t.indexOf(Math.min(...t)); }; // Tek gecis: her ailenin erisim gecmisi taranir, ozetler ve strateji toplamlari birlikte cikar. const OZET = [], STRATEJI = { "hepsi sik": 0, "hepsi seyrek": 0, "hepsi arsiv": 0, "aileye gore": 0, "nesne basina": 0 }; let hicOkunmayan = 0, hicOkunmayanBayt = 0, hicOkunmayanFazla = 0; let sicakArsiv = 0, sicakArsivOkuma = 0, sicakArsivBayt = 0, sicakArsivFazla = 0, getirilenBayt = 0; for (const f of AILE) { let okumaTop = 0, sifir = 0, dagilim = [0, 0, 0, 0], nesneBasina = 0; const aileTop = SINIF.map(() => 0), esik = 24; // yilda 24'ten cok okunan nesne sicak sayilir for (const r of erisim(f)) { okumaTop += r; if (r === 0) sifir++; const i = enUcuz(f.ort, r); dagilim[i]++; nesneBasina += yillik(SINIF[i], f.ort, r); for (let k = 0; k < SINIF.length; k++) aileTop[k] += yillik(SINIF[k], f.ort, r); if (r === 0) { // hic okunmayan nesne sik sinifta tutulursa hicOkunmayan++; hicOkunmayanBayt += f.ort; hicOkunmayanFazla += yillik(SINIF[0], f.ort, 0) - yillik(SINIF[3], f.ort, 0); } else if (r > esik) { // sicak nesne arsive atilirsa sicakArsiv++; sicakArsivOkuma += r; sicakArsivBayt += f.ort; getirilenBayt += r * f.ort; sicakArsivFazla += yillik(SINIF[3], f.ort, r) - yillik(SINIF[0], f.ort, r); } } const en = aileTop.indexOf(Math.min(...aileTop)); STRATEJI["hepsi sik"] += aileTop[0]; STRATEJI["hepsi seyrek"] += aileTop[1]; STRATEJI["hepsi arsiv"] += aileTop[3]; STRATEJI["aileye gore"] += aileTop[en]; STRATEJI["nesne basina"] += nesneBasina; OZET.push({ f, okumaTop, sifir, dagilim }); } console.log(`erisim gecmisi modeldir; uretec bu dosyada, tohum ${TOHUM}. Yillik okuma sayilari:`); console.log("\naile | hic okunmayan | ort. okuma/yil | nesne basina en ucuz sinif dagilimi"); console.log(" | | | sik | seyrek | cok seyrek | arsiv"); console.log("-------------------|---------------|----------------|---------|--------|------------|-------"); for (const o of OZET) console.log(`${o.f.ad.padEnd(18)} | %${g(100 * o.sifir / o.f.adet, 12, 1)} | ${g(o.okumaTop / o.f.adet, 14, 2)} | ` + o.dagilim.map((x, i) => String(x).padStart([7, 6, 10, 6][i])).join(" | ")); console.log("\nstrateji | yillik toplam | en iyiye gore fazla | fazlanin orani"); console.log("-------------|---------------|---------------------|---------------"); const enIyi = STRATEJI["nesne basina"]; for (const [ad, t] of Object.entries(STRATEJI)) console.log(`${ad.padEnd(12)} | ${g(t, 13)} | ${g(t - enIyi, 19)} | %${g(100 * (t - enIyi) / enIyi, 13, 1)}`); console.log("\nyanlis sinifin iki yonu:"); console.log(` seyrek veri sik sinifta: ${hicOkunmayan.toLocaleString("tr")} nesne, ` + `${g(GIB(hicOkunmayanBayt), 0, 1)} GiB hic okunmuyor; arsiv yerine sik sinifta tutmak ` + `yilda ${g(hicOkunmayanFazla, 0, 2)} birim fazla.`); console.log(` sik veri arsiv sinifinda: ${sicakArsiv.toLocaleString("tr")} nesne, ` + `${g(GIB(sicakArsivBayt), 0, 1)} GiB, yilda ${sicakArsivOkuma.toLocaleString("tr")} okuma; ` + `arsivde tutmak yilda ${g(sicakArsivFazla, 0, 2)} birim fazla.`); console.log(` bu okumalarda getirilen ${g(GIB(getirilenBayt), 0, 1)} GiB internete de cikiyorsa cikis ucreti ` + `ayrica ${g(GIB(getirilenBayt) * CIKIS_INTERNET, 0, 2)} birim: getirme ucreti cikisin yerine gecmez, ustune biner.`); // Sinif degistirmek geri alinabilir, bedava degil: en az saklama suresi tasiyor. const f = AILE[1], N = 12_000, GUN = 8, seyrek = SINIF[1]; const gecisUcret = 2 * N * seyrek.gecis; const enAzBorc = N * seyrek.saklama * GIB(f.ort) * (seyrek.enAz / 30); const getirmeUcret = N * GIB(f.ort) * seyrek.getirme; const kazanc = N * (SINIF[0].saklama - seyrek.saklama) * GIB(f.ort) * (GUN / 30); console.log(`\n${N.toLocaleString("tr")} fatura belgesi seyrek sinifa tasindi, ${GUN}. gun sicak oldugu anlasildi, geri alindi:`); console.log(` iki yonlu gecis istegi ${g(gecisUcret, 0, 3)} | en az ${seyrek.enAz} gun saklama borcu ${g(enAzBorc, 0, 3)} | ` + `geri getirme ${g(getirmeUcret, 0, 3)}`); console.log(` toplam ${g(gecisUcret + enAzBorc + getirmeUcret, 0, 3)} birim; ${GUN} gunde kazanilan ${g(kazanc, 0, 3)} birim; ` + `net ${g(gecisUcret + enAzBorc + getirmeUcret - kazanc, 0, 3)} birim zarar.`); console.log(` karar geri alindi, ucreti geri alinmadi: en az saklama borcu nesne silinse de duruyor.`);
erisim gecmisi modeldir; uretec bu dosyada, tohum 40127. Yillik okuma sayilari:
aile | hic okunmayan | ort. okuma/yil | nesne basina en ucuz sinif dagilimi
| | | sik | seyrek | cok seyrek | arsiv
-------------------|---------------|----------------|---------|--------|------------|-------
sayac-okumalari | % 86.4 | 3.66 | 168911 | 0 | 0 | 1071089
fatura-belgeleri | % 61.4 | 4.16 | 8311 | 1095 | 3774 | 72820
saha-fotograflari | % 97.0 | 1.22 | 920 | 64 | 142 | 40874
is-emri-kayitlari | % 45.7 | 7.34 | 105257 | 63012 | 0 | 141731
strateji | yillik toplam | en iyiye gore fazla | fazlanin orani
-------------|---------------|---------------------|---------------
hepsi sik | 53.91 | 45.78 | % 563.0
hepsi seyrek | 38.39 | 30.26 | % 372.2
hepsi arsiv | 48.65 | 40.52 | % 498.4
aileye gore | 16.80 | 8.67 | % 106.6
nesne basina | 8.13 | 0.00 | % 0.0
yanlis sinifin iki yonu:
seyrek veri sik sinifta: 1.306.318 nesne, 166.2 GiB hic okunmuyor; arsiv yerine sik sinifta tutmak yilda 43.87 birim fazla.
sik veri arsiv sinifinda: 84.485 nesne, 4.6 GiB, yilda 6.192.838 okuma; arsivde tutmak yilda 35.56 birim fazla.
bu okumalarda getirilen 276.3 GiB internete de cikiyorsa cikis ucreti ayrica 23.48 birim: getirme ucreti cikisin yerine gecmez, ustune biner.
12.000 fatura belgesi seyrek sinifa tasindi, 8. gun sicak oldugu anlasildi, geri alindi:
iki yonlu gecis istegi 0.240 | en az 30 gun saklama borcu 0.054 | geri getirme 0.043
toplam 0.338 birim; 8 gunde kazanilan 0.012 birim; net 0.326 birim zarar.
karar geri alindi, ucreti geri alinmadi: en az saklama borcu nesne silinse de duruyor.
Yanlış Sınıfın İki Yönü
Dağılım sütunu başabaş tablosunu doğruluyor: sayaç okumalarında ara sınıflar sıfır nesne alıyor, 1.240.000 kayıt sık erişilen ile arşiv arasında ikiye bölünüyor. Nesne büyüdükçe ara sınıfların aralığı açılıyor ve fatura belgelerinde dördü de kullanılıyor.
Strateji tablosu tek sınıflı üç yaklaşımın da kaybettiğini gösteriyor. Hepsini sık sınıfta tutmak en iyi dağıtımın 6,6 katıdır. Hepsini arşive atmak, sezgiye aykırı biçimde, hepsini seyrek sınıfta tutmaktan pahalıdır — 48,65’e karşı 38,39 — çünkü sıcak nesnelerin getirme ücreti kazanılanı yutar. Aile düzeyinde tek sınıf toplamı 16,80’e indirir ama hâlâ yüzde 106 fazladır; kazancın yarısı aile içindeki sıcak-soğuk ayrımındadır.
İki yön ayrı sayılıyor. Hiç okunmayan 1.306.318 nesne — 166,2 GiB — sık sınıfta tutulduğunda yılda 43,87 birim fazla ödetir; bu yön sessizdir, hiçbir hata üretmez. Ters yön daha az veri, daha çok para tutar: yılda 24’ten çok okunan 84.485 nesne yalnız 4,6 GiB’dir ama arşivde yılda 35,56 birim fazla ödetir. Getirilen 276,3 GiB internete de çıkıyorsa 23,48 birimlik çıkış ücreti bunun üstüne biner.
Geri Alınan Karar, Geri Alınmayan Ücret
Depolama sınıfı, anahtar düzeninin aksine geri alınabilir görünür: bir istek nesneyi başka sınıfa taşır. Son blok bedelini sayıyor. 12.000 fatura belgesi seyrek sınıfa taşınıyor, sekizinci gün geri alınıyor: 0,240 geçiş isteği, 0,054 en az saklama borcu, 0,043 geri getirme, toplam 0,338 birim. Sekiz günde kazanılan 0,012 birimdir; net zarar kazancın yirmi yedi katıdır.
Geri alınamayan kalem en az saklama borcudur. Nesne sınıfa girdiği anda o sınıfın süresi taahhüt edilir; nesne taşınsa da silinse de borç durur. Devredilen taraf sabittir: dayanıklılık, çoğaltma ve yerleşim dört sınıfta da sağlayıcıdadır.
Özet
- Sınıf bir kalite kadranı değil takastır: saklama düştükçe istek ve getirme yükselir, dayanıklılık değişmez.
- Başabaş sıklık nesne boyutuna bağlıdır: 380 KiB’lik belgede yılda 10,9 okuma, 2,3 KiB’lik kayıtta 0,5 okuma. Küçük nesnelerde ara sınıflar hiç kazanmaz.
- Nesne başına seçim 8,13 birim tutar; hepsini sık sınıfta tutmak 53,91, hepsini arşive atmak 48,65 birimdir — arşiv otomatik olarak ucuz değildir.
- Yanlış sınıf iki yönde ödetir: hiç okunmayan 166,2 GiB sık sınıfta yılda 43,87 birim, çok okunan 4,6 GiB arşivde 35,56 birim fazla. Getirme ücreti çıkışın üstüne biner.
- Sınıf kararı geri alınabilir, ücreti alınamaz: sekiz günde dönülen geçiş kazancın yirmi yedi katı zarar bırakır; en az saklama borcu nesne silinse de durur.
Sonraki Adım
Bu dersin sınıf dağıtımı elle yapıldı: her nesnenin yıllık okuma sayısı bilindi ve en ucuz sınıf seçildi. Gerçek bir kovada bu bilgi önceden yoktur; her nesneyi tek tek taşıyan bir betik, taşıdığı isteklerin ücretiyle kazancı götürür. Sonraki ders kararı depoya bırakır: yaşam döngüsü kuralı nesneleri yaşlarına göre sınıf değiştirir ve siler. Ölçülecek üç şey var — kural olmadan biriken bayt, kuralla geçen ve silinen nesne sayısı, erken geçişin geri getirme bedeli. Sürümleme de aynı derste açılır: açıldığında silme işleminin gerçekten silmediği sayıyla gösterilir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.