İçeriğe geç
academia.sh

Ders 02 / 18

Çoğaltma

Aynı verinin birden çok yerde tutulmasının ölçekleme karşılığı: ana–kopya düzeninde okumanın bölünüp yazmanın her düğümde tekrarlanması, kopya eklemenin azalan kazancı, saklanan verinin kopya sayısıyla çarpılması, asenkron gerikliğin bayat takip yanıtına çevrilmesi ve çok ana düzende iki bölgenin aynı gönderiye yazmasının kalıcı ayrışma olarak sayılması.

İçindekiler

Önceki ders deponun türünü erişim örüntüsüne bağladı ve okuma yolunu kısalttı. Ortada hâlâ tek bir kopya var: saniyede 236,11 işlem alan, 718,24 GB taşıyan ve düştüğünde takip sorgusunun da ücretlendirmenin de yanıtsız kaldığı tek bir düğüm. Yükü birden çok makineye dağıtmanın ilk yolu veriyi bölmek değil, çoğaltmaktır (replication): aynı veriyi birden çok yerde tutmak.

Çoğaltmanın mekaniği İlişkisel Veritabanı Yönetimi kursunda kuruldu — günlük akışı, çoğaltma gecikmesi, senkron onayın bedeli, çoğunluk kuralı ve devralma orada anlatıldı ve ölçüldü. Burada mekanik tekrarlanmaz. Bu dersin sorusu bir ölçekleme kararıdır: kopya eklemek hangi sayıyı düşürür, hangi sayıyı düşürmez ve karşılığında ne büyür. Tutarlılık modellerinin tanımları da Sistem Tasarımına Giriş kursunda kuruldu; burada yalnız kullanılırlar.

Çoğaltma Neyi Böler

Ana–kopya (leader–follower) düzeninde bir düğüm yazmaları kabul eder, ötekiler o düğümün değişikliklerini uygular ve okumaları karşılar. Bölünen şey okumadır; yazma bölünmez, çünkü her düğüm her yazmayı uygulamak zorundadır. Aşağıdaki hesap bu asimetriyi kopya sayısına göre açar ve ikinci tabloda asenkron gerikliğin okuma tarafındaki karşılığını verir.

VD2 — bir gönderinin yolda kalma süresi 3 gün. Gerekçe: kabul ile teslim arasında geçen süre; V4’ün yedi durum olayı bu süreye yayılır ve takip sorguları bu süredeki gönderilere gelir. Duyarlılığı doğrusaldır — süre iki katına çıkarsa yoldaki gönderi sayısı iki katına çıkar ve bayat yanıt oranı yarıya iner. Bu varsayım K01’in tablosuna eklenmez.

// cogaltma/ana-kopya.mjs — okuma kopyalara bolunurken yazmanin her dugumde tekrarlanmasi,
// ve asenkron gerikligin bayat takip yanitina cevrilmesi. Hepsi aritmetiktir.
const OKUMA = 41.67;        // K01: onbellek arkasi okuma/s
const YAZMA = 194.44;       // 01. ders: ayrilmis duzende depo yazma islem/s
const SAKLAMA = 718.24;     // 01. ders: saklanan veri GB
const OLAY = 97.22;         // K01: tepe yazma istek/s (durum olayi)
const V3 = 400_000;         // K01: gunluk gonderi
const VD2 = 3;              // bu dersin varsayimi: gonderinin yolda kalma suresi (gun)
const YOLDA = V3 * VD2, GUN = 86_400;
const b = (x, n = 2) => x.toFixed(n);

console.log(`${"kopya".padStart(6)}${"kopya basina okuma/s".padStart(21)}${"dugum basina yazma/s".padStart(21)}` +
  `${"dugum yuku".padStart(12)}${"yazma/okuma".padStart(13)}${"kopya kazanci".padStart(15)}${"toplam GB".padStart(11)}`);
let onceki = null;
for (const n of [1, 2, 3, 4, 5]) {
  const okuma = OKUMA / n, yuk = YAZMA + okuma;
  console.log(`${String(n).padStart(6)}${b(okuma).padStart(21)}${b(YAZMA).padStart(21)}` +
    `${b(yuk).padStart(12)}${b(YAZMA / okuma).padStart(13)}` +
    `${(onceki === null ? "-" : b(onceki - yuk)).padStart(15)}${b(SAKLAMA * (1 + n)).padStart(11)}`);
  onceki = yuk;
}
console.log(`tek dugum (01. ders): yuk ${b(YAZMA + OKUMA)} islem/s, yazma/okuma ${b(YAZMA / OKUMA)}, ` +
  `${b(SAKLAMA)} GB`);

console.log(`\nyolda olan gonderi = ${YOLDA.toLocaleString("tr-TR")} (VD2 = ${VD2} gun)`);
console.log(`${"gerilik s".padStart(10)}${"penceredeki degisen gonderi".padStart(28)}` +
  `${"bayat okuma orani".padStart(19)}${"bayat yanit/s".padStart(15)}${"bayat yanit/gun".padStart(17)}`);
for (const L of [0.5, 1, 5, 10]) {
  const degisen = OLAY * L, oran = degisen / YOLDA, bayat = OKUMA * oran;
  console.log(`${b(L, 1).padStart(10)}${b(degisen).padStart(28)}${oran.toExponential(2).padStart(19)}` +
    `${b(bayat, 5).padStart(15)}${b(bayat * GUN, 1).padStart(17)}`);
}
 kopya kopya basina okuma/s dugum basina yazma/s  dugum yuku  yazma/okuma  kopya kazanci  toplam GB
     1                41.67               194.44      236.11         4.67              -    1436.48
     2                20.84               194.44      215.28         9.33          20.84    2154.72
     3                13.89               194.44      208.33        14.00           6.95    2872.96
     4                10.42               194.44      204.86        18.66           3.47    3591.20
     5                 8.33               194.44      202.77        23.33           2.08    4309.44
tek dugum (01. ders): yuk 236.11 islem/s, yazma/okuma 4.67, 718.24 GB

yolda olan gonderi = 1.200.000 (VD2 = 3 gun)
 gerilik s penceredeki degisen gonderi  bayat okuma orani  bayat yanit/s  bayat yanit/gun
       0.5                       48.61            4.05e-5        0.00169            145.8
       1.0                       97.22            8.10e-5        0.00338            291.7
       5.0                      486.10            4.05e-4        0.01688           1458.4
      10.0                      972.20            8.10e-4        0.03376           2916.8

Bu sayılar hesap sınıfındadır; girdileri K01’in hesapları, 01. dersin sonuçları ve VD2’dir.

Sayıların Okunması

Kopya eklemek yazma yükünü hiç düşürmüyor. Düğüm başına yazma sütunu beş satırda da 194,44 işlem/s. Bu bir taban değil, tavanla ilgisi olmayan bir zemin: kopya sayısı ne olursa olsun her düğüm her durum olayını uygular. Bölünen tek sütun okumadır ve o da 41,67’den başlıyor.

Kazanç hızla tükeniyor. Birinci kopyadan ikinciye geçiş düğüm yükünü 20,84 işlem/s düşürüyor; ikinciden üçüncüye 6,95, dördüncüden beşinciye 2,08. Beşinci kopya ilk kopyanın kazandırdığının onda birini kazandırıyor, çünkü indirilebilen tek pay okumadır ve okuma zaten yükün yüzde 18’i. Ana–kopya çoğaltması bu yüzden okuma ağırlıklı bir yükün aracıdır; K01’in hesabı ise deponun gördüğü yerde yazma ağırlıklıdır ve 01. dersin belge güncellemesi bu ağırlığı büyütmüştü.

depoda yazma/okuma orani her kopyada büyüyor. K01’de 2,33’tü, 01. dersten sonra 4,67 oldu, üç kopyada 14,00’e çıkıyor. Oran bir bozulma göstergesi değil, düzenin tanımıdır: okuma dağıldıkça her düğümün gördüğü karışım yazmaya kayar. Bir düğümün yazma için ayarlanması gerektiği kararı bu sütundan okunur.

saklanan veri GB doğrudan çarpılıyor. Tek düğümde 718,24 GB olan veri, üç kopyayla 2872,96 GB, beş kopyayla 4309,44 GB oluyor. Çoğaltmanın faturası budur ve K01’in saklama satırını en çok oynatan karardır: 712,48 GB’lik özgün hesap altı katına çıkabiliyor.

Gerilik ikinci tabloda bir yanıt sayısına dönüşüyor. Bir saniyelik çoğaltma gecikmesinde penceredeki değişen gönderi 97,22’dir; yoldaki 1.200.000 gönderiye oranı 8,10e-5 ve depoya ulaşan 41,67 okumanın karşılığı günde 291,7 bayat takip yanıtıdır. Beş saniyede 1458,4. Sayı küçüktür ve küçüklüğünün nedeni de tablodadır: bayat yanıt oranı, değişim hızının yoldaki gönderi sayısına oranıdır ve yoldaki gönderi sayısı büyüktür. Aynı hesap kısa ömürlü ve çok değişen bir veri kümesinde başka bir sonuç verirdi. Kopyadan okumanın kabul edilebilirliği bu yüzden bir ilke değil, bu bölmenin sonucudur.

Kopyanın ikinci karşılığı hizmet erişilebilirliğidir: ana düştüğünde kopyalardan biri yükseltilir. Devralma ve çoğunluk kuralı İlişkisel Veritabanı Yönetimi kursunda ölçüldü ve burada tekrarlanmaz.

Çok Ana ve Çakışma

Yazma tek düğümde kaldığı sürece yazma ölçeklenmez. Çok ana (multi-leader) düzeni bu kısıtı kaldırır: birden çok düğüm yazma kabul eder ve birbirinin değişikliklerini uygular. Karşılığında tek ananın bedava verdiği bir şey kaybolur — yazmaların tek bir sırası.

Örüntü bunu somutlaştırıyor. P2, taşıyıcıya göre durum olayı yazmasıdır ve bir gönderi aktarma anında iki bölgenin arasındadır: çıkış bölgesi “aktarmaya verildi”, varış bölgesi “aktarmadan alındı” olayını birkaç saniye arayla yazar.

VD3 — bir gönderinin iki bölgeden yakın zamanlı yazma alma oranı 0,02. Gerekçe: yedi olayın biri aktarma anına düşer ve o anda iki bölge de yazar. Duyarlılığı doğrusaldır; aşağıda 0,04 ile verilir. K01’in tablosuna eklenmez.

// cogaltma/cok-ana.mjs — iki ananin ayni gonderiye yazmasi. Bu bir MODELDIR: tur soyut bir
// adimdir, yayilma turu bir parametredir, olculmus sure degildir. Sayilan sey, yayilma
// bittikten sonra iki dugumun ayni son durumu gosterip gostermedigidir.
const OLAY = 20_000, GONDERI = 4000, YAYILMA = 3;   // model parametreleri
const VD3 = 0.02;            // varsayim: gonderinin iki bolgeden yakin zamanli yazma alma orani
let s = 20260730 % 2147483647;                       // sabit tohum: her kosumda ayni dizi
const rast = () => (s = (s * 48271) % 2147483647) / 2147483647;

const olaylar = [];
for (let t = 0; t < OLAY; t += 1) {
  const g = Math.floor(rast() * GONDERI), bolge = rast() < 0.5 ? "A" : "B";
  olaylar.push({ t, g, bolge, ad: `o${t}` });
  if (rast() < VD3) {                                // aktarma ani: karsi bolge de yaziyor
    const gecikme = 1 + Math.floor(rast() * YAYILMA);
    olaylar.push({ t: t + gecikme, g, bolge: bolge === "A" ? "B" : "A", ad: `o${t}b` });
  }
}
const gonderiOlay = new Map();
for (const o of olaylar) (gonderiOlay.get(o.g) ?? gonderiOlay.set(o.g, []).get(o.g)).push(o);
let cakisan = 0;
for (const liste of gonderiOlay.values())
  if (liste.some((a) => liste.some((c) => a.bolge !== c.bolge && Math.abs(a.t - c.t) <= YAYILMA))) cakisan += 1;

function kosum(kural) {
  const dugum = { A: new Map(), B: new Map() };
  for (const d of ["A", "B"]) {
    const gelen = olaylar.map((o) => ({ varis: o.bolge === d ? o.t : o.t + YAYILMA, yerel: o.bolge === d ? 1 : 0, o }));
    gelen.sort((x, y) => x.varis - y.varis || y.yerel - x.yerel);   // varis sirasi, yerel once
    for (const { o } of gelen) {
      if (kural === "son-yazan") { dugum[d].set(o.g, o.ad); continue; }
      const k = dugum[d].get(o.g) ?? dugum[d].set(o.g, []).get(o.g);   // ekleme: olay kumesi
      k.push(o);
    }
  }
  const durum = (d, g) => {
    const v = dugum[d].get(g);
    if (kural === "son-yazan") return v;
    return v.slice().sort((x, y) => y.t - x.t || (x.ad < y.ad ? 1 : -1))[0].ad;   // belirlenimli kural
  };
  let ayrisan = 0;
  for (const g of dugum.A.keys()) if (durum("A", g) !== durum("B", g)) ayrisan += 1;
  const kayit = kural === "son-yazan" ? dugum.A.size
    : [...dugum.A.values()].reduce((a, v) => a + v.length, 0);
  return { ayrisan, kayit, dokunulan: dugum.A.size };
}

console.log(`model: ${olaylar.length} olay, ${gonderiOlay.size} gonderi dokunuldu, ` +
  `yayilma ${YAYILMA} tur, VD3 = ${VD3}`);
console.log(`iki bolgeden yakin zamanli yazma alan gonderi = ${cakisan} ` +
  `(dokunulanlarin %${((100 * cakisan) / gonderiOlay.size).toFixed(2)}'si)`);
console.log(`\n${"birlestirme kurali".padEnd(22)}${"ayrisan gonderi".padStart(17)}` +
  `${"cakisanlarin payi".padStart(19)}${"dugumde tutulan kayit".padStart(23)}`);
for (const kural of ["son-yazan", "ekleme"]) {
  const r = kosum(kural);
  console.log(`${kural.padEnd(22)}${String(r.ayrisan).padStart(17)}` +
    `${(r.ayrisan / cakisan).toFixed(4).padStart(19)}${String(r.kayit).padStart(23)}`);
}

const GUNLUK_OLAY = 2_800_000, V4 = 7;                // K01: gunluk durum olayi, olay/gonderi
const oran = kosum("son-yazan").ayrisan / gonderiOlay.size;
console.log(`\nK01 olcegine tasima: gunde ${GUNLUK_OLAY.toLocaleString("tr-TR")} olay ve ` +
  `${(GUNLUK_OLAY / V4).toLocaleString("tr-TR")} gonderi (V4 = ${V4})`);
console.log(`son-yazan kuralinda gunde ${Math.round((GUNLUK_OLAY / V4) * oran).toLocaleString("tr-TR")} ` +
  `gonderi iki bolgede farkli son durum gosterir; VD3 = 0,04 olsaydi yaklasik iki kati`);
model: 20365 olay, 3971 gonderi dokunuldu, yayilma 3 tur, VD3 = 0.02
iki bolgeden yakin zamanli yazma alan gonderi = 363 (dokunulanlarin %9.14'si)

birlestirme kurali      ayrisan gonderi  cakisanlarin payi  dugumde tutulan kayit
son-yazan                            66             0.1818                   3971
ekleme                                0             0.0000                  20365

K01 olcegine tasima: gunde 2.800.000 olay ve 400.000 gonderi (V4 = 7)
son-yazan kuralinda gunde 6.648 gonderi iki bolgede farkli son durum gosterir; VD3 = 0,04 olsaydi yaklasik iki kati

Model üç şey söylüyor. Birincisi, çakışma seyrek değildir: gönderi başına ortalama beş olay düştüğünde, olay başına yüzde ikilik bir aktarma oranı gönderilerin yüzde 9,14’ünü iki bölgeden yazılan gönderi hâline getiriyor.

İkincisi, çakışan yazmaların hepsi ayrışma üretmiyor — 363 gönderiden 66’sı, yani beşte biri. Ayrışma yalnız iki olayın iki düğüme farklı sırayla ulaştığı durumda doğuyor. Son yazanın kazandığı kuralda düğüm A yerel olayını önce uygular, düğüm B karşıdakini; yayılma bittiğinde iki düğümde iki farklı son durum kalır ve kendiliğinden düzelmez. K01 ölçeğine taşındığında günde 6648 gönderi, iki bölgede farklı son durum gösterir. VD3 iki katına çıkarsa sayı da yaklaşık iki katına çıkar.

Üçüncüsü ve tasarım açısından önemlisi: ayrışma verinin değil, saklama biçiminin sonucudur. Ekleme kuralında aynı olay dizisi sıfır ayrışma veriyor, çünkü birleştirme işlemi kümelerin birleşimidir ve birleşim sıradan bağımsızdır. Son durum, kümeden belirlenimli bir kuralla türetilir ve iki düğüm aynı kümeye sahip olduğu için aynı sonucu bulur. Bedeli son sütundadır: düğümde tutulan kayıt 3971’den 20.365’e çıkıyor, 5,13 kat. Bu kursun akışında bedel kâğıt üzerindedir, çünkü K01 zaten bütün olayları saklıyor (V6, V12); asıl değişen, son durumun üzerine yazılan bir alan olmaktan çıkıp türetilen bir değer olmasıdır.

P2’nin biçimi bu yüzden belirleyicidir. Durum olayı ekleme olduğu için çok ana düzenine uyar; üzerine yazılan bir alan olsaydı uymazdı. Çok ana kararı veriyi değil, yazmanın biçimini sorgular. Yayılma bittikten sonra kopyaların aynı yere gelmesi sonunda tutarlılığın söz verdiği şeydir ve tanımı Sistem Tasarımına Giriş kursunda verildi; bu ölçüm o sözün ancak birleştirme kuralı sıradan bağımsız olduğunda tutulduğunu gösteriyor.

Özet

  • Ana–kopya düzeninde okuma bölünür, yazma bölünmez: düğüm başına yazma beş kopyada da 194,44 işlem/s’de kalıyor, kopya başına okuma 41,67’den 8,33’e iniyor.
  • Kopya eklemenin kazancı hızla tükeniyor: düğüm yükü birinci eklemede 20,84 işlem/s düşerken dördüncüde 2,08 düşüyor; depoda yazma/okuma orani 4,67’den üç kopyada 14,00’e çıkıyor.
  • saklanan veri GB doğrudan çarpılıyor: 718,24 GB üç kopyayla 2872,96, beş kopyayla 4309,44 GB.
  • Gerilik bir yanıt sayısına çevrilir: VD2 = 3 gün ile yoldaki 1.200.000 gönderide bir saniyelik gecikme günde 291,7, beş saniyelik gecikme 1458,4 bayat takip yanıtı üretir.
  • Çok anada VD3 = 0,02 ile gönderilerin yüzde 9,14’ü iki bölgeden yazılıyor; son yazanın kazandığı kuralda bunların beşte biri kalıcı ayrışma veriyor, K01 ölçeğinde günde 6648 gönderi.
  • Ayrışma saklama biçiminden doğuyor: ekleme kuralında sıfır ayrışma çıkıyor, bedeli düğümde tutulan kaydın 3971’den 20.365’e (5,13 kat) çıkmasıdır.

Sonraki Adım

Ç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 ve bunu 01. dersin tarama ölçümü göstermişti. Sonraki ders veriyi anahtara göre değil işleve göre bölmeyi ele alır: iki bağlam kendi deposuna ayrıldığında her deponun aldığı istek ve taşıdığı veri ne oluyor, yazma yolu kaç depoya uzuyor, iki bağlama birden dokunan sorgu kaç depoya gidiyor ve ortak alanların yinelenmesi saklanan veriye ne ekliyor.

İlerlemeni kaydetmek ve not almak için Giriş yap

Notlarım

Not almak için giriş yapmalısın.

Aramak için yazmaya başlayın.

↑↓ Esc gezin · aç · kapat