Ders 03 / 18
Federasyon
Verinin anahtara göre değil işleve göre bölünmesi: ücretlendirme ile teslimat operasyonu bağlamlarının kendi depolarına ayrılması, iki bağlama birden dokunan sorgunun dokunduğu depo ve depolar arasında taşınan anahtar sayısının gerçek bir motorda ölçülmesi, ortak alanların yinelenmesinin saklanan veriye eklediği payın hesaplanması ve toplu taramanın çevrimiçi depodan çıkarılması.
İçindekiler
Çoğaltma okumayı böldü ama her düğüme aynı 194,44 yazmayı bıraktı ve saklanan veriyi kopya sayısıyla çarptı. İki sınır da aynı yerden geliyor: bütün veri her yerde duruyor. Oysa deponun taşıdığı veri tek bir bütün değil. Ücretlendirmenin sorduğu sorularla teslimat operasyonunun sorduğu sorular farklı kayıtlara dokunuyor; 01. dersin tarama ölçümü bunu göstermişti — dönem taraması olay ayrıntısına hiç ihtiyaç duymadan 21.000 kayıttan 3000 kayda inmişti.
Federasyon (federation), veriyi anahtara göre değil işleve göre bölmektir: her bağlam kendi deposunu alır ve o depo yalnız o bağlamın sorularına hizmet eder. Ayrım anahtarla ilgili değildir — aynı takip numarası iki depoda da geçebilir; ayrım, o numaranın hangi soruyla sorulduğuyla ilgilidir.
İki Bağlam, İki Depo
Gönderi takip ve ücretlendirme servisinde iki bağlam vardır. Teslimat operasyonu P1 ile P2’yi taşır: takip numarasına göre tekil okuma ve taşıyıcıdan gelen durum olayı yazması. Ücretlendirme P3’ü taşır: satıcıya göre dönemsel toplu tarama, tarife, sözleşme ve fatura satırı.
Bölme kusursuz değildir, çünkü iki bağlam bazı kayıtlara birlikte ihtiyaç duyar. Ücretlendirme gönderinin ne zaman kabul edildiğini ve ne zaman teslim edildiğini bilmek zorundadır, ama aradaki rota adımlarını bilmesi gerekmez.
VD4 — ücretlendirmeyi ilgilendiren olay oranı 2/7. Gerekçe: fatura kabul ve teslim zamanına bağlıdır; V4’ün yedi olayından yalnız ikisi ücreti etkiler. Duyarlılığı aşağıda 3/7 ile verilir.
VD5 — gönderi kaydı iki bağlama bölündüğünde payları 500 ve 600 bayttır. Gerekçe: V7’nin 900 baytlık kaydında ağırlık, hacim, sözleşme ve tarife ücretlendirmeye, bölge ve rota alanları operasyona gider; takip numarası, satıcı kimliği ve dönem etiketi ise iki depoda da durmak zorunda olduğu için toplam 1100 bayta çıkar, yinelenen pay 200 bayttır. Duyarlılığı aşağıda 400 bayt yinelenme ile verilir.
İki varsayım da K01’in tablosuna eklenmez.
Bölünen Sorgu Kaç Depoya Dokunuyor
Aşağıdaki düzenek üç depoyu bellek içinde kurar — federasyonsuz tek depo ile iki federe depo — ve aynı soruları her ikisine sorar. Ölçülen şey dokunulan depo sayısı, işlenen kayıt ve federe düzende depolar arasında taşınan anahtar sayısıdır.
// federasyon/capraz.mjs — ayni sorgularin tek depoda ve iki federe depoda dokundugu depo, // isledigi kayit ve depolar arasinda tasidigi anahtar. node:sqlite ile gercek bir motor kosar. import { DatabaseSync } from "node:sqlite"; const SATICI = 20, GUN = 30, GUNLUK = 100, OLAY = 7, SON = OLAY - 1; // model olcegi const [tek, ucret, ops] = [0, 1, 2].map(() => new DatabaseSync(":memory:")); tek.exec(`CREATE TABLE gonderi(no TEXT PRIMARY KEY, satici INT, gun INT, tarife INT, bolge INT); CREATE INDEX g_satici ON gonderi(satici, gun); CREATE TABLE olay(no TEXT, sira INT, durum TEXT); CREATE INDEX o_no ON olay(no);`); ucret.exec(`CREATE TABLE gonderi(no TEXT PRIMARY KEY, satici INT, gun INT, tarife INT); CREATE INDEX g_satici ON gonderi(satici, gun); CREATE TABLE olay(no TEXT, sira INT, durum TEXT); CREATE INDEX o_no ON olay(no);`); ops.exec(`CREATE TABLE gonderi(no TEXT PRIMARY KEY, satici INT, bolge INT); CREATE TABLE olay(no TEXT, sira INT, durum TEXT); CREATE INDEX o_no ON olay(no);`); const sayac = { tek: 0, ucret: 0, ops: 0 }; for (const [ad, db] of [["tek", tek], ["ucret", ucret], ["ops", ops]]) db.function("say", { deterministic: false }, (x) => { sayac[ad] += 1; return x; }); const toplam = () => sayac.tek + sayac.ucret + sayac.ops; const gT = tek.prepare("INSERT INTO gonderi VALUES(?,?,?,?,?)"), oT = tek.prepare("INSERT INTO olay VALUES(?,?,?)"); const gU = ucret.prepare("INSERT INTO gonderi VALUES(?,?,?,?)"), oU = ucret.prepare("INSERT INTO olay VALUES(?,?,?)"); const gO = ops.prepare("INSERT INTO gonderi VALUES(?,?,?)"), oO = ops.prepare("INSERT INTO olay VALUES(?,?,?)"); for (const d of [tek, ucret, ops]) d.exec("BEGIN"); for (let s = 0; s < SATICI; s += 1) for (let d = 0; d < GUN; d += 1) for (let k = 0; k < GUNLUK; k += 1) { const no = `TR-${s}-${d}-${k}`; gT.run(no, s, d, 100 + s, d % 5); gU.run(no, s, d, 100 + s); gO.run(no, s, d % 5); for (let i = 0; i < OLAY; i += 1) { oT.run(no, i, `durum${i}`); oO.run(no, i, `durum${i}`); if (i === 0 || i === SON) oU.run(no, i, `durum${i}`); // VD4: yalniz kabul ve teslim } } for (const d of [tek, ucret, ops]) d.exec("COMMIT"); const adet = (db, t) => db.prepare(`SELECT count(*) c FROM ${t}`).get().c; console.log(`model: ${adet(tek, "gonderi")} gonderi; olay kaydi -> tek depo ${adet(tek, "olay")}, ` + `teslimat ${adet(ops, "olay")}, ucretlendirme ${adet(ucret, "olay")}`); const capraz = () => { // anahtarlari tasiyip ikinci depoya sormak const a = ucret.prepare("SELECT say(no) AS no FROM gonderi WHERE satici = ?").all(10).map((r) => r.no); const q = ops.prepare(`SELECT say(no) FROM olay WHERE no = ? AND sira = ${SON}`); for (const k of a) q.all(k); return a.length; }; const IS = [ ["P1 tekil okuma / federe", 1, () => (ops.prepare("SELECT say(no) FROM olay WHERE no = ? ORDER BY sira DESC LIMIT 3").all("TR-10-15-50"), 0)], ["P3 donem taramasi / federe", 1, () => (ucret.prepare("SELECT say(no) FROM gonderi WHERE satici = ? AND gun BETWEEN 0 AND 29").all(10), 0)], ["S4 son rota adimi / tek depo", 1, () => (tek.prepare( `SELECT say(g.no), o.durum FROM gonderi g JOIN olay o ON o.no = g.no WHERE g.satici = ? AND o.sira = ${SON}`).all(10), 0)], ["S4 son rota adimi / federe", 2, capraz], ["S5 teslim tarihine gore ucret / federe", 1, () => (ucret.prepare( `SELECT say(g.no), g.tarife FROM gonderi g JOIN olay o ON o.no = g.no WHERE g.satici = ? AND o.sira = ${SON}`).all(10), 0)], ["S5 teslim olayi kopyalanmasaydi", 2, capraz], ]; console.log(`\n${"sorgu / duzen".padEnd(40)}${"depo".padStart(6)}${"islenen kayit".padStart(15)}${"tasinan anahtar".padStart(17)}`); for (const [ad, depo, calis] of IS) { sayac.tek = 0; sayac.ucret = 0; sayac.ops = 0; const anahtar = calis(); console.log(`${ad.padEnd(40)}${String(depo).padStart(6)}${String(toplam()).padStart(15)}${String(anahtar).padStart(17)}`); } const bayt = Buffer.byteLength("TR-10-15-50"); console.log(`\nanahtar ${bayt} bayt: federe duzende S4 icin depolar arasinda ` + `${(3000 * bayt) / 1000} kB tasiniyor, tek depoda 0`);
model: 60000 gonderi; olay kaydi -> tek depo 420000, teslimat 420000, ucretlendirme 120000 sorgu / duzen depo islenen kayit tasinan anahtar P1 tekil okuma / federe 1 7 0 P3 donem taramasi / federe 1 3000 0 S4 son rota adimi / tek depo 1 3000 0 S4 son rota adimi / federe 2 6000 3000 S5 teslim tarihine gore ucret / federe 1 3000 0 S5 teslim olayi kopyalanmasaydi 2 6000 3000 anahtar 11 bayt: federe duzende S4 icin depolar arasinda 33 kB tasiniyor, tek depoda 0
Bu sayılar ölçüm sınıfındadır; bu makinede koşan bir motordan gelirler ve belirlenimlidirler.
İlk iki satır iyi haberi veriyor: bağlamın kendi soruları kendi deposunda kalıyor ve tek depodaki maliyetleriyle aynı. Federasyon bu sorulara ne kazandırıyor ne kaybettiriyor.
Dört ve beşinci satırlar asıl bilgiyi taşıyor. S4 — bir satıcının dönemindeki gönderilerin son rota adımı iki bağlamın kesişiminde duruyor: satıcı ve dönem ücretlendirmenin, rota adımı operasyonun alanıdır. Tek depoda bu tek bir sorgudur, motor birleştirmeyi kendi içinde yapar ve 3000 kayıt işler. Federe düzende sorgu ikiye bölünüyor: ücretlendirmeden 3000 anahtar okunuyor, uygulama bunları taşıyıp teslimat deposuna soruyor ve toplam işlenen kayıt 6000’e çıkıyor. Kaybolan şey birleştirmenin kendisidir; yerine geçen şey depolar arasında taşınan 3000 anahtar, yani 33 kilobayttır. Federasyonun uygulamaya asıl dayattığı bedel budur — motorun bedava yaptığı işi uygulama kodu para vererek yapar.
Son iki satır VD4’ün gerekçesini ölçüyor. S5 — teslim tarihine göre ücret, teslim olayının kopyası ücretlendirme deposunda olduğu için tek depoda kalıyor ve 3000 kayıtla bitiyor. Kopya olmasaydı aynı soru S4 ile aynı biçime düşer: 2 depo, 6000 kayıt, 3000 taşınan anahtar. Yani yinelenen 2/7 olay, bir çapraz sorguyu tek depoya indiriyor. Bir bağlam sınırının doğru çizilip çizilmediğinin ölçüsü budur: sınırın doğru yerde olması, çapraz soru sayısının küçük olmasıdır.
Hesaba Geri Dönüş
Yinelemenin bedeli K01’in iki büyüme satırında toplanıyor.
// federasyon/hesap.mjs — iki baglama bolunen deponun K01 satirlarindaki karsiligi const V3 = 400_000, V4 = 7, V6 = 220, V7 = 900, V8 = 3, V12 = 730; // K01 varsayimlari const OKUMA = 41.67, OLAY_YAZMA = 97.22, TARAMA = 833.33; // K01 hesaplari const ARTIS = 976, SAKLAMA = 712.48; // K01 hesaplari const BELGE = 194.44; // 01. ders: durum olayi teslimat tarafinda 2 hedefe yaziliyor const VD4 = 2 / 7; // varsayim: ucretlendirmeyi ilgilendiren olay orani const VD5 = [500, 600]; // varsayim: bolunen gonderi kaydinin paylari (ucret, ops) bayt const GONDERI_YAZMA = 13.89; // K01 turevi: (V3 / 86.400) x V8 = gonderi olusturma, tepe kayit/s const b = (x, n = 2) => x.toFixed(n); const ops = GONDERI_YAZMA + BELGE + OKUMA; const uc = GONDERI_YAZMA + OLAY_YAZMA * VD4; console.log(`${"depo".padEnd(24)}${"istek+islem/s".padStart(14)}${"tarama kayit/s".padStart(16)}` + `${"gunluk MB".padStart(11)}${"saklanan GB".padStart(13)}`); const opsMB = (V3 * VD5[1] + V3 * V4 * V6) / 1e6, ucMB = (V3 * VD5[0] + V3 * V4 * VD4 * V6) / 1e6; console.log(`${"teslimat operasyonu".padEnd(24)}${b(ops).padStart(14)}${"0".padStart(16)}` + `${b(opsMB).padStart(11)}${b((opsMB * V12) / 1000).padStart(13)}`); console.log(`${"ucretlendirme".padEnd(24)}${b(uc).padStart(14)}${b(TARAMA).padStart(16)}` + `${b(ucMB).padStart(11)}${b((ucMB * V12) / 1000).padStart(13)}`); console.log(`${"tek depo (federasyonsuz)".padEnd(24)}${b(GONDERI_YAZMA + BELGE + OKUMA).padStart(14)}` + `${b(TARAMA).padStart(16)}${b(ARTIS).padStart(11)}${b(SAKLAMA).padStart(13)}`); const toplamMB = opsMB + ucMB; console.log(`\ntoplam islem ${b(ops + uc)} /s = tek deponun ${b((ops + uc) / (GONDERI_YAZMA + BELGE + OKUMA))} kati`); console.log(`gunluk yazilan ${b(toplamMB)} MB (K01: ${ARTIS}) = ${b(toplamMB / ARTIS)} kat`); console.log(`saklanan ${b((toplamMB * V12) / 1000)} GB (K01: ${SAKLAMA}) = ${b((toplamMB * V12) / 1000 / SAKLAMA)} kat`); const yol = (GONDERI_YAZMA * 2 + OLAY_YAZMA * (1 + VD4)) / (GONDERI_YAZMA + OLAY_YAZMA); console.log(`yazma yolunun uzunlugu = ${b(yol, 3)} depo (gonderi 2, durum olayi ${b(1 + VD4, 3)})`); const duyarli = (vd4, yin) => ((V3 * (V7 + yin) + V3 * V4 * V6 * (1 + vd4)) / 1e6); console.log(`\nduyarlilik: VD4 = 3/7 -> gunluk ${b(duyarli(3 / 7, 200))} MB, ` + `saklanan ${b((duyarli(3 / 7, 200) * V12) / 1000)} GB`); console.log(` yinelenen kimlik 400 bayt -> gunluk ${b(duyarli(VD4, 400))} MB, ` + `saklanan ${b((duyarli(VD4, 400) * V12) / 1000)} GB`); console.log(`ucu birden: 01'in belge deposu 5,76 GB eklenir, 02'nin uc kopyasiyla ` + `${b(3 * ((toplamMB * V12) / 1000 + 5.76))} GB`);
depo istek+islem/s tarama kayit/s gunluk MB saklanan GB
teslimat operasyonu 250.00 0 856.00 624.88
ucretlendirme 41.67 833.33 376.00 274.48
tek depo (federasyonsuz) 250.00 833.33 976.00 712.48
toplam islem 291.67 /s = tek deponun 1.17 kati
gunluk yazilan 1232.00 MB (K01: 976) = 1.26 kat
saklanan 899.36 GB (K01: 712.48) = 1.26 kat
yazma yolunun uzunlugu = 1.375 depo (gonderi 2, durum olayi 1.286)
duyarlilik: VD4 = 3/7 -> gunluk 1320.00 MB, saklanan 963.60 GB
yinelenen kimlik 400 bayt -> gunluk 1312.00 MB, saklanan 957.76 GB
ucu birden: 01'in belge deposu 5,76 GB eklenir, 02'nin uc kopyasiyla 2715.36 GB
Bu satırlar hesap sınıfındadır.
Toplu tarama çevrimiçi depodan çıkıyor. Teslimat operasyonu deposunun tarama sütunu sıfırdır; saniyede 833,33 kayıt okuyan dört saatlik iş artık takip sorgusuyla aynı diski, aynı belleği ve aynı kilit yöneticisini paylaşmıyor. Federasyonun ölçekleme gerekçesi buradadır ve tek bir sayıyla söylenebilir: çevrimiçi yolun gördüğü kayıt hızı 833,33’ten sıfıra iniyor.
Toplam işlem 1,17 katına çıkıyor. 250,00 işlem/s’lik tek depo, 250,00 ve 41,67’ye bölünüyor; fazla olan 41,67 ücretlendirmenin kendi kopyalarını yazmasıdır. Yazma yolunun uzunluğu 1,375 depoya çıkıyor: gönderi oluşturma iki depoya, durum olayı ortalama 1,286 depoya yazılıyor. Yazma yolunun uzaması bir dayanıklılık sorusu doğurur — iki depodan biri yazmayı kabul etmezse ne olur — ama o soru bu kursun konusu değildir.
gunluk veri artisi MB ve saklanan veri GB birlikte 1,26 katına çıkıyor. 976 MB 1232 MB
oluyor, 712,48 GB 899,36 GB oluyor. Artışın kaynağı ikidir ve ayrı ayrı okunabilir: yinelenen
kimlik alanları gönderi kaydını 900 bayttan 1100 bayta çıkarıyor, yinelenen kabul ve teslim
olayları günde 176 MB ekliyor. VD4 üçte bire çıkarsa saklanan veri 963,60 GB, yinelenen kimlik 400
bayta çıkarsa 957,76 GB olur; iki varsayım da aynı büyüklükte etkilidir.
Son satır üç kararın birleşimini veriyor. 01. dersin belge deposu, bu dersin federasyonu ve 02. dersin üç kopyası birlikte uygulandığında saklanan veri 2715,36 GB’dir — K01’in 712,48 GB’sinin 3,81 katı. Veri katmanı kararlarının ortak yönü budur: hepsi aynı sayıyı büyütür ve etkileri çarpışır, toplanmaz.
Özet
- Federasyon veriyi anahtara göre değil işleve göre böler; sınır bağlamın sorularıyla çizilir, aynı takip numarası iki depoda da geçebilir.
- Bağlamın kendi soruları kendi deposunda kalıyor, ama iki bağlamın kesişimindeki S4 sorusu 1 depodan 2 depoya, 3000 kayıttan 6000 kayda çıkıyor ve depolar arasında 3000 anahtar (33 kB) taşınıyor.
- Yinelenen 2/7 olay bir çapraz soruyu (S5) tek depoya indiriyor; sınırın doğruluğunun ölçüsü çapraz soru sayısıdır.
- Toplu tarama çevrimiçi depodan çıkıyor: teslimat operasyonu deposunun gördüğü tarama hızı 833,33 kayıt/s’den sıfıra iniyor.
- Toplam işlem 250,00’den 291,67 işlem/s’ye (1,17 kat), yazma yolu 1,375 depoya uzuyor.
- Yineleme K01’in iki büyüme satırını 1,26 katına çıkarıyor (976 → 1232 MB, 712,48 → 899,36 GB); 01 ve 02’nin kararlarıyla birlikte saklanan veri 2715,36 GB olur.
Sonraki Adım
Federasyon deponun işini böldü ama her bağlamın kendi verisi hâlâ bütün olarak tek bir düğümde duruyor. Teslimat operasyonu deposu 624,88 GB taşıyor ve saniyede 250,00 işlem alıyor; bu sayılar bağlam bölünerek küçülmez, çünkü artık bölünecek bağlam kalmadı. Kalan tek yol veriyi kendi içinde bölmektir: aynı bağlamın kayıtlarını bir anahtara göre bağımsız düğümlere dağıtmak. Sonraki ders bu kararı ele alır — anahtar nasıl seçilir, seçim üç erişim örüntüsünün her birinin dokunduğu düğüm sayısını nasıl değiştirir, en yüklü düğüm ötekilerin kaç katını taşır ve düğüm sayısı değiştiğinde kaç bayt yer değiştirir.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.