Ders 11 / 22
Ekleme Günlüğü Kalıcılığı
Durumun değil yazmanın kaydedildiği kalıcılık biçimi: komut günlüğünün yazma başına kaç bayt tuttuğu, günlüğün anlık görüntüye oranla nasıl büyüdüğü, yeniden yazımın kazandırdığı yer ve her komutta, saniyede bir ya da işletim sistemine bırakarak eşzamanlamanın kayıp penceresiyle yazma maliyetine etkisi.
İçindekiler
Anlık görüntü, deponun bir andaki hâlini yazar. İki an arasında olan biten hiçbir yere kaydedilmez, bu yüzden kayıp penceresi aralık ne kadar kısaltılırsa kısaltılsın sıfıra inmez. Kütüphanenin ödünç sayaçları için bu, en kötü durumda binlerce ödünç kaydının elle geri toplanması demektir.
Bu ders ikinci biçimi ele alır: durumu değil yazmayı kaydetmek. Her değiştirici komut, uygulandığı sırayla bir dosyanın sonuna eklenir. Kurtarma, boş bir depo üzerinde bu komutları yeniden oynatır. Dosyanın sonuna eklemekten başka bir işlem yapılmadığı için yazma yolu ucuzdur; bedeli iki yerde çıkar: dosya durmadan büyür ve “eklendi” ile “diskte” arasındaki fark bir ayara dönüşür.
Yazmanın Kendisini Kaydetmek
Kayıt biçimi deponun komutunu olduğu gibi taşır: komut harfi, anahtar, değer. Yük önceki dersle aynıdır — yirmi bin üyeye dağılan altmış bin ödünç yazması, aynı tohumla — böylece iki biçimin sayıları doğrudan karşılaştırılabilir.
// gunluk.mjs — komut günlüğü kaydı ve aynı ödünç yükü export const kayit = (a, d) => `Y\t${a}\t${d}\n`; // Y: yazma komutu export function uretec(tohum) { let a = tohum >>> 0; return () => { a = (a + 0x6d2b79f5) >>> 0; let t = a; t = Math.imul(t ^ (t >>> 15), t | 1); t ^= t + Math.imul(t ^ (t >>> 7), t | 61); return ((t ^ (t >>> 14)) >>> 0) / 4294967296; }; } export function yukUret(sayi, tohum, uyeSayisi = 20000) { const r = uretec(tohum), liste = []; for (let i = 0; i < sayi; i++) { const u = Math.floor(r() * uyeSayisi); liste.push([ 'odunc:u' + String(u).padStart(5, '0'), `sube=${1 + (u % 8)};gun=${[7, 14, 28][i % 3]};yenileme=${i % 4}`, ]); } return liste; }
Günlüğün Büyümesi ve Yeniden Yazım
Aşağıdaki koşum yükü gerçekten diske yazar ve yirmi binde bir durup iki büyüklüğü yan yana koyar: o ana kadarki günlük dosyası ve aynı durumun anlık görüntüsü.
// buyume.mjs — günlüğün büyümesi ve yeniden yazımın kazandırdığı yer import { appendFileSync, writeFileSync, statSync, rmSync } from 'node:fs'; import { kayit, yukUret } from './gunluk.mjs'; const YUK = yukUret(60000, 20260731); rmSync('odunc.log', { force: true }); const durum = new Map(); let tampon = '', gunlukBayt = 0; console.log('yazma günlük bayt anahtar görüntü bayt günlük/görüntü'); for (let i = 0; i < YUK.length; i++) { const [a, d] = YUK[i]; tampon += kayit(a, d); // her yazma günlüğün sonuna eklenir durum.set(a, d); if ((i + 1) % 20000 === 0) { appendFileSync('odunc.log', tampon); gunlukBayt += Buffer.byteLength(tampon); tampon = ''; let g = 0; for (const [k, v] of durum) g += Buffer.byteLength(k) + Buffer.byteLength(v) + 2; console.log(String(i + 1).padStart(5), String(gunlukBayt).padStart(12), String(durum.size).padStart(8), String(g).padStart(13), (gunlukBayt / g).toFixed(2).padStart(15)); } } console.log('dosya:', statSync('odunc.log').size, 'bayt,', YUK.length, 'kayıt,', (statSync('odunc.log').size / YUK.length).toFixed(1), 'bayt/yazma'); let yeni = ''; // yeniden yazım: yalnız yaşayan anahtarlar for (const [a, d] of durum) yeni += kayit(a, d); writeFileSync('odunc-yeni.log', yeni); const eski = statSync('odunc.log').size, sonra = statSync('odunc-yeni.log').size; console.log('yeniden yazım:', durum.size, 'kayıt,', sonra, 'bayt |', 'kazanç', eski - sonra, 'bayt (%' + (((eski - sonra) / eski) * 100).toFixed(1) + ')');
yazma günlük bayt anahtar görüntü bayt günlük/görüntü 20000 793333 12717 479015 1.66 40000 1586666 17330 652842 2.43 60000 2380000 19040 717260 3.32 dosya: 2380000 bayt, 60000 kayıt, 39.7 bayt/yazma yeniden yazım: 19040 kayıt, 755340 bayt | kazanç 1624660 bayt (%68.3)
Yazma başına maliyet sabittir: 39,7 bayt. Günlük dosyası yazma sayısıyla doğrusal büyür ve neyi yazdığına bakmaz; aynı üyenin ödünç kaydının onuncu kez güncellenmesi de dosyaya tam bir kayıt ekler. Anlık görüntü ise anahtar sayısıyla büyür ve üzerine yazılan değerlerin geçmişini taşımaz. Son sütun aradaki makası verir: yirmi bin yazmada günlük görüntünün 1,66 katıdır, altmış binde 3,32 katı. Kütüphane çalışmayı sürdürdükçe oran büyümeye devam eder, çünkü paydaki sayı durmaz, paydadaki sayı yirmi bin üyede doyar.
Bu büyüme, ekleme günlüğünün doğrudan sonucudur ve tek çaresi vardır: yeniden yazım. Deponun o anki durumu taranır ve yaşayan her anahtar için tek bir kayıt üretilir; eski dosya bununla değiştirilir. Ölçümde 60.000 kayıt 19.040 kayda iner, dosya 2.380.000 bayttan 755.340 bayta düşer, kazanç yüzde 68,3’tür.
İki ayrıntı burada okunur. Birincisi, yeniden yazılmış günlük (755.340 bayt) aynı durumun anlık görüntüsünden (717.260 bayt) tam 38.080 bayt büyüktür; bu, 19.040 kaydın her birine düşen iki baytlık komut önekidir. Yeniden yazımdan sonra günlük, biçim farkı dışında bir anlık görüntüdür. İkincisi ve daha önemlisi: yeniden yazım da bütün girişleri taramak zorundadır. Yani önceki dersteki görüntü alma maliyeti — duraklatmak ya da kopyalamak — bu yolda da vardır, yalnız daha seyrek ortaya çıkar. Ekleme günlüğü görüntü almayı ortadan kaldırmaz; ne zaman yapılacağını dosyanın boyutuna bağlar.
Eşzamanlama Ayarı
Bir kaydın “yazıldı” sayılması üç ayrı yerde gerçekleşebilir. Kayıt önce uygulamanın kendi tamponuna girer. Tampon boşaltıldığında işletim sisteminin tamponuna geçer; süreç bu noktadan sonra çökse bile kayıt yaşar, çünkü onu artık işletim sistemi tutmaktadır. Kaydın kalıcı ortama geçtiğinin garantilenmesi ise ayrı bir istektir; makinenin elektriği kesildiğinde yalnız o adımı geçmiş kayıtlar durur.
Ayar bu üç durak arasında seçim yapar. Aşağıdaki koşum aynı bin beş yüz kaydı üç kiple yazar ve koşumdan bağımsız nicelikleri basar: yazma çağrısı sayısı, eşzamanlama sayısı, dosya boyutu. Süre de ölçülür, ama tek başına bir sayı olarak değil sıralama olarak basılır; süre diskin türüne ve makinenin yüküne bağlıdır.
KB3. Kütüphanenin en yoğun saatinde depoya saniyede 3.000 yazma gelir. KB4. Uygulama tamponu 65.536 bayttır ve işletim sistemi kendi tamponunu beş saniyede bir kalıcı ortama geçirir.
// eszamanlama.mjs — üç eşzamanlama ayarının maliyeti ve kayıp penceresi import { openSync, writeSync, fsyncSync, closeSync, statSync, rmSync } from 'node:fs'; import { kayit, yukUret } from './gunluk.mjs'; const N = 1500, HIZ = 3000, TAMPON = 65536, GERIYAZMA = 5; // KB3, KB4 const YUK = yukUret(N, 20260731); function kos(ad, donem, tamponla) { rmSync(ad, { force: true }); const fd = openSync(ad, 'w'); let cagri = 0, es = 0, t = '', tb = 0; const bas = process.hrtime.bigint(); for (let i = 0; i < N; i++) { const s = kayit(YUK[i][0], YUK[i][1]); if (tamponla) { // uygulama tamponu dolunca yazılır, eşzamanlama yok t += s; tb += Buffer.byteLength(s); if (tb >= TAMPON) { writeSync(fd, t); cagri++; t = ''; tb = 0; } } else { writeSync(fd, s); cagri++; if ((i + 1) % donem === 0) { fsyncSync(fd); es++; } } } if (t) { writeSync(fd, t); cagri++; } if (!tamponla && N % donem !== 0) { fsyncSync(fd); es++; } const sure = Number(process.hrtime.bigint() - bas); closeSync(fd); return { cagri, es, bayt: statSync(ad).size, sure }; } const kipler = [ ['her yazmada', kos('a.log', 1, false)], ['saniyede bir', kos('b.log', HIZ, false)], ['işletim sistemine bırak', kos('c.log', 0, true)], ]; console.log(`ölçüm: ${N} kayıt, aynı içerik, üç kip`); console.log('kip yazma çağrısı eşzamanlama dosya baytı'); for (const [ad, s] of kipler) console.log(ad.padEnd(24), String(s.cagri).padStart(13), String(s.es).padStart(12), String(s.bayt).padStart(12)); console.log('süre sıralaması (yavaştan hızlıya):', [...kipler].sort((x, y) => y[1].sure - x[1].sure).map(([a]) => a).join(', ')); const bpk = kipler[0][1].bayt / N; // kayıt başına bayt const T = 60000; console.log(`\n${T} yazmalık yük, saniyede ${HIZ} yazma, ${TAMPON} baytlık tampon,`, `${GERIYAZMA} saniyelik geri yazma`); console.log('kip eşzamanlama en kötü kayıp ortalama kayıp'); const satir = (ad, es, kotu) => console.log(ad.padEnd(24), String(es).padStart(11), String(kotu).padStart(14), String(Math.round(kotu / 2)).padStart(15)); satir('her yazmada', T, 0); satir('saniyede bir', Math.ceil(T / HIZ), HIZ); satir('işletim sistemine bırak', 0, Math.floor(TAMPON / bpk) + GERIYAZMA * HIZ);
ölçüm: 1500 kayıt, aynı içerik, üç kip kip yazma çağrısı eşzamanlama dosya baytı her yazmada 1500 1500 59500 saniyede bir 1500 1 59500 işletim sistemine bırak 1 0 59500 süre sıralaması (yavaştan hızlıya): her yazmada, saniyede bir, işletim sistemine bırak 60000 yazmalık yük, saniyede 3000 yazma, 65536 baytlık tampon, 5 saniyelik geri yazma kip eşzamanlama en kötü kayıp ortalama kayıp her yazmada 60000 0 0 saniyede bir 20 3000 1500 işletim sistemine bırak 0 16652 8326
Üç kip aynı dosyayı üretir: 59.500 bayt, aynı içerik. Ayrılan tek şey, o baytların ne zaman ve hangi garantiyle yerine geçtiğidir.
Her yazmada eşzamanlama, altmış bin yazmalık yükte altmış bin ayrı eşzamanlama demektir ve kayıp penceresini sıfıra indirir. Bu ölçümün alındığı makinede eşzamanlama başına yaklaşık 3,8 milisaniye harcandı; sayı diske ve yüke göre değişir, bu yüzden çıktıya süre değil sıralama basıldı. Değişmeyen nicelik eşzamanlama sayısıdır ve tabloda üç kip arasındaki fark dört büyüklük mertebesindedir. Sıfır kayıp penceresinin bir koşulu daha vardır: eşzamanlama, yanıt istemciye dönmeden önce bitmelidir. Kayıt diske geçmeden “ödünç alındı” yanıtı verilirse pencere yeniden açılır.
Saniyede bir eşzamanlama, aynı yükte yirmi eşzamanlamaya iner; yazma yolundan üç bin kat daha az bekleme çıkarır. Karşılığında en kötü durumda bir saniyelik iş, yani 3.000 ödünç yazması yiter. Ortalama kayıp bunun yarısıdır.
İşletim sistemine bırakmak eşzamanlamayı tümüyle kaldırır ve yazma çağrısı sayısını da düşürür: bin beş yüz kayıt tek bir çağrıyla yazıldı, çünkü hepsi 65.536 baytlık tampona sığdı. En ucuz kip budur ve en kötü kaybı da odur: 16.652 yazma. Bu sayının nereden geldiği önemli, çünkü iki ayrı gecikme toplanıyor — uygulama tamponundaki 1.652 kayıt ve işletim sistemine verilmiş olup henüz kalıcı ortama geçmemiş beş saniyelik iş.
Ayarın Karşılığı
Üç ayar aynı sorunun üç yanıtıdır: bir ödünç yazmasının kaybının bedeli, o yazmayı diske geçirmenin bedelinden büyük mü? Kütüphanenin oturum kayıtları için değildir; oturumu yiten görevli yeniden açar. Ödünç sayaçları için sınırdadır. Ceza hesabına giren gecikme kayıtları için büyüktür, çünkü kaybedilen kayıt paraya karşılık gelir ve geri toplanamaz.
Ekleme günlüğünün anlık görüntüye karşı asıl üstünlüğü, kayıp penceresini yazma başına ayarlanabilir kılmasıdır. Bedeli de dosyanın boyutudur: aynı durum için 3,32 kat yer, ve o yeri geri almak için düzenli yeniden yazım. İki biçimin birbirinin eksiğini kapattığı buradan görünür, ama hangisinin ne zaman seçileceği henüz tek bir tabloda toplanmadı.
Özet
- Ekleme günlüğü durumu değil komutu kaydeder; ölçümde yazma başına 39,7 bayt tuttu ve dosya yazma sayısıyla doğrusal büyüdü.
- Günlük yazma sayısıyla, anlık görüntü anahtar sayısıyla büyür: aynı veri için oran 20.000 yazmada 1,66, 60.000 yazmada 3,32 kat oldu.
- Yeniden yazım 60.000 kaydı 19.040 kayda indirdi ve dosyayı yüzde 68,3 küçülttü; sonuçta ortaya çıkan dosya, iki baytlık komut öneki dışında bir anlık görüntüdür ve aynı tam tarama maliyetini taşır.
- Eşzamanlama ayarı kayıp penceresini belirler: her yazmada 0 yazma ve 60.000 eşzamanlama; saniyede bir 3.000 yazma ve 20 eşzamanlama; işletim sistemine bırakıldığında 16.652 yazma ve hiç eşzamanlama.
- Üç kip aynı 59.500 baytlık dosyayı üretti; aralarındaki fark içerikte değil, kaydın hangi durakta durduğu garantisindedir.
Sonraki Adım
İki kalıcılık biçimi de ölçüldü ve ikisinin de kendi başına eksik kaldığı görüldü: anlık görüntü kayıp penceresini kapatamıyor, ekleme günlüğü boyutunu tutamıyor. Sonraki ders seçimi tek bir tabloda toplar. Üç ölçü karşılaştırılır: sürecin yeniden başlamasından hizmete hazır olmasına kadarki kurtarma adımı, kayıp penceresi ve sürekli maliyet. İki yolun birlikte kullanıldığı düzen de aynı tabloya girer — görüntü tabanı, üzerine son görüntüden sonraki kayıtlar — ve bu düzenin kurtarmayı neden hem kısalttığı hem de kayıp penceresini dar tuttuğu sayıyla gösterilir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.