Ders 08 / 18
Depo Türleri
Anahtar–değer, belge, geniş sütun ve çizge deposunun aynı erişim örüntüleriyle ölçülmesi: her türün bir örüntü için gerektirdiği sorgu sayısı ile dokunduğu kayıt sayısı, bir örüntüyü ucuzlatan yapının hangi örüntüyü pahalılaştırdığı ve seçimin giriş kursundaki depo istek hızı ile yazma/okuma oranına ödettiği bedel.
İçindekiler
Buraya kadarki bütün kararlar satırları ve sütunları olan tek bir depo biçimi üzerinde verildi. Oysa ölçülen örüntülerin istediği şeyler birbirinden çok farklıdır: takip sorgusu tek anahtarla tek kayıt ister, durum olayı sürekli ekleme yapar, dönem taraması geniş bir aralıkta toplam alır. Bir depo türü bu işlerden birini yapısına yazdığında ötekileri uygulamaya bırakır ve bu ders o alışverişi sayar.
Ailelerin tanımı — anahtar–değer, belge, geniş sütun ve çizge deposunun neyi bırakıp neyi verdiği — Veri Modelleme ve İlişkisel Kuram kursunda kuruldu; ilişkisel ile ilişkisel olmayan seçimi ve kayıt sisteminin tek kalması kuralı bu konunun ilk dersinde ölçüldü. İkisi de tekrarlanmaz. Buradaki soru dardır: her tür, her örüntü için kaç sorgu ister ve kaç kayda dokunur.
Türleri Ayıran Şey
Bir depo türünü, yerel olarak yanıtlayabildiği erişim biçimi tanımlar. Yanıtlayamadığı her biçim uygulamaya düşer ve uygulamaya düşen her adım ayrı bir depo isteğidir.
Anahtar–değer deposu yalnız birincil anahtarı bilir. Değerin içi kendisi için anlamsızdır, bu yüzden ikincil erişim uygulamanın tuttuğu bir anahtar listesiyle yapılır ve listedeki her kayıt ayrı bir istek olur.
Belge deposu değerin alanlarını görür ve bir alan üzerinde ikincil dizin kurabilir. İkincil bir süzme tek istekte biter.
Geniş sütun deposu satırları bölüm anahtarına göre gruplar ve grup içinde sıralı bir aralığı tek istekte okur. Farklı bir anahtarla süzme, aynı verinin ikinci bir bölüm anahtarıyla ikinci kez yazılmasını gerektirir.
Çizge deposu düğüm ve kenarları birinci sınıf nesne yapar; bir dolaşma depo içinde yürür ve istemciye tek istek olarak görünür.
Ölçüm için üç örüntünün yanına dördüncüsü konur, çünkü ilk üçünün hiçbiri çizge deposunun kazandığı yerde durmaz. P4 — rota dolaşması: bir aktarma merkezi durduğunda o merkezden geçen gönderilerin bulunması, sonraki duraklarının çıkarılması ve o duraklardan geçen gönderilere kadar üç adım ilerlenmesi. Teslimat operasyonunun sorduğu bir sorudur ve bir kaydın kendisiyle değil kayıtlar arasındaki bağla ilgilidir.
// tur/erisim.mjs — ayni erisim oruntulerinin dort depo turunde gerektirdigi sorgu ve // dokundugu kayit. MODEL: depolar birer nesnedir, sayaclar aciktir; bir urun degil, // erisim yeteneklerinin modelidir. Sonuclar belirlenimlidir. const GONDERI = 60_000, SATICI = 20, MERKEZ = 8; // 01. dersin olcegi: satici basina 3000 const gonderi = Array.from({ length: GONDERI }, (_, i) => ({ no: `TR-${i}`, satici: i % SATICI, rota: [0, 1, 2].map((j) => (i * 3 + j) % MERKEZ), })); const saticiDizin = new Map(), merkezDizin = new Map(), rotaSonraki = new Map(); for (const g of gonderi) { if (!saticiDizin.has(g.satici)) saticiDizin.set(g.satici, []); saticiDizin.get(g.satici).push(g); for (let j = 0; j < g.rota.length; j += 1) { const m = g.rota[j]; if (!merkezDizin.has(m)) merkezDizin.set(m, []); merkezDizin.get(m).push(g); if (j + 1 < g.rota.length) { if (!rotaSonraki.has(m)) rotaSonraki.set(m, new Set()); rotaSonraki.get(m).add(g.rota[j + 1]); } } } class Depo { // her cagri bir depo istegidir; dokunulan kayit sayilir constructor() { this.sorgu = 0; this.kayit = 0; } iste(n) { this.sorgu += 1; this.kayit += n; return n; } } // P1: takip numarasina gore tekil okuma (durum + son uc rota adimi) // P2: tasiyicidan gelen bir durum olayinin yazilmasi // P3: bir saticinin donem taramasi // P4: bir merkezden baslayip H adim ilerleyen rota dolasmasi const H = 3; // dolasma derinligi const ORUNTU = { "anahtar-deger": { // yalniz birincil anahtar; ikincil dizin uygulamada P1: (d) => d.iste(1), // deger butun halinde gelir P2: (d) => { d.iste(1); d.iste(1); }, // oku, sonra butun degeri yaz P3: (d) => { const n = saticiDizin.get(3).length; d.iste(1); // once dizin anahtari for (let i = 0; i < n; i += 1) d.iste(1); }, // sonra kayit basina bir istek P4: (d, derinlik) => { let m = new Set([0]); for (let h = 0; h < derinlik; h += 1) { const s = new Set(); for (const x of m) { for (const g of merkezDizin.get(x)) d.iste(1); for (const y of rotaSonraki.get(x) ?? []) s.add(y); d.iste(1); } m = s; } }, }, "belge": { // belge alanlari uzerinde ikincil dizin P1: (d) => d.iste(1), P2: (d) => d.iste(1), // belgenin olay dizisine ekleme P3: (d) => d.iste(saticiDizin.get(3).length), P4: (d, derinlik) => { let m = new Set([0]); for (let h = 0; h < derinlik; h += 1) { const s = new Set(); let k = 0; for (const x of m) { k += merkezDizin.get(x).length; for (const y of rotaSonraki.get(x) ?? []) s.add(y); } d.iste(k); m = s; } }, }, "genis sutun": { // bolum anahtari + bolum icinde sirali aralik P1: (d) => d.iste(3), // son uc olay satiri P2: (d) => { d.iste(1); d.iste(1); }, // olay tablosu + satici tablosu P3: (d) => d.iste(saticiDizin.get(3).length), P4: (d, derinlik) => { let m = new Set([0]); for (let h = 0; h < derinlik; h += 1) { const s = new Set(); let k = 0; for (const x of m) { k += merkezDizin.get(x).length; for (const y of rotaSonraki.get(x) ?? []) s.add(y); } d.iste(k); m = s; } }, }, "cizge": { // dugum ve kenarlar; dolasma depo icinde yurur P1: (d) => d.iste(1 + 3), // gonderi dugumu + son uc kenar P2: (d) => d.iste(2), // olay dugumu + kenar P3: (d) => d.iste(1 + 2 * saticiDizin.get(3).length), // satici dugumu, kenar, gonderi P4: (d, derinlik) => { let m = new Set([0]), k = 0; for (let h = 0; h < derinlik; h += 1) { const s = new Set(); for (const x of m) { k += 1 + merkezDizin.get(x).length; for (const y of rotaSonraki.get(x) ?? []) s.add(y); } m = s; } d.iste(k); }, }, }; console.log(`model: ${GONDERI} gonderi, ${SATICI} satici, ${MERKEZ} merkez, ` + `rota 3 adim, dolasma ${H} adim; sayilar sorgu/kayit`); const ad = ["P1 tekil okuma", "P2 olay yazma", "P3 donem taramasi", "P4 rota dolasmasi"]; console.log(`\n${"depo turu".padEnd(16)}${ad.map((a) => a.padStart(19)).join("")}`); for (const [tur, o] of Object.entries(ORUNTU)) { const hucre = ["P1", "P2", "P3", "P4"].map((p) => { const d = new Depo(); o[p](d, H); return `${d.sorgu}/${d.kayit}`; }); console.log(tur.padEnd(16) + hucre.map((h) => h.padStart(19)).join("")); } console.log(`\nP4 dolasma derinligi ile sorgu sayisi`); console.log(`${"depo turu".padEnd(16)}${[1, 2, 3].map((h) => `${h} adim`.padStart(10)).join("")}`); for (const [tur, o] of Object.entries(ORUNTU)) console.log(tur.padEnd(16) + [1, 2, 3].map((h) => { const d = new Depo(); o.P4(d, h); return String(d.sorgu).padStart(10); }).join(""));
model: 60000 gonderi, 20 satici, 8 merkez, rota 3 adim, dolasma 3 adim; sayilar sorgu/kayit depo turu P1 tekil okuma P2 olay yazma P3 donem taramasi P4 rota dolasmasi anahtar-deger 1/1 2/2 3001/3001 67503/67503 belge 1/1 1/1 1/3000 3/67500 genis sutun 1/3 2/2 1/3000 3/67500 cizge 1/4 1/2 1/6001 1/67503 P4 dolasma derinligi ile sorgu sayisi depo turu 1 adim 2 adim 3 adim anahtar-deger 22501 45002 67503 belge 1 2 3 genis sutun 1 2 3 cizge 1 1 1
Sayılar hesap sınıfındadır: modelin sayaçlarından çıkarlar, belirlenimlidirler ve makineden bağımsızdırlar. Model bir ürün değil, erişim yeteneklerinin bir soyutlamasıdır.
Hiçbir Sütun Baştan Sona Kazanmıyor
P1 hiçbir türü ayırmıyor. Dört türün de sorgu sayısı 1. Dokunulan kayıt 1, 1, 3 ve 4 olarak farklılaşıyor, ama tek anahtarla tek kayıt getirmek her türün yaptığı iştir. Bir tasarımı yalnız takip sorgusuna bakarak seçmek, dört seçeneği ayırt edemez.
P2 iki türü ayırıyor. Belge ve çizge deposu bir olayı tek istekte ekliyor. Anahtar–değer deposu iki istek istiyor, çünkü değerin içine dokunamıyor: bütün değeri okuyup bütün değeri geri yazıyor. Geniş sütun deposu da iki istek istiyor, ama başka bir nedenle — P3’ü karşılamak için aynı veri ikinci bir bölüm anahtarıyla ikinci bir tabloya da yazılıyor. İki tür aynı sayıyı iki ayrı gerekçeyle üretiyor ve gerekçe, sayının nasıl büyüyeceğini belirliyor: anahtar–değerin ikinci isteği kaydın boyutuyla, geniş sütunun ikinci isteği desteklenen sorgu sayısıyla büyür.
P3 anahtar–değer deposunu eliyor. Belge ve geniş sütun tek istekte 3000 kayıt okuyor; çizge tek istekte 6001 kayda dokunuyor, çünkü satıcı düğümünden her gönderiye önce bir kenar, sonra bir düğüm üzerinden geçiliyor. Anahtar–değer 3001 istek istiyor: ikincil dizin uygulamada tutulduğu için listede ne varsa tek tek çekiliyor. Dokunulan kayıt sayısı aynı büyüklükte, istek sayısı üç bin kat farklı.
P4 tabloyu tersine çeviriyor. Dokunulan kayıt dört türde de aynı mertebede: 67.500 ile 67.503 arası. Ayrım sorgu sütununda. Belge ve geniş sütun adım başına bir istek harcıyor; çizge deposu derinlik ne olursa olsun tek istekte bitiriyor; anahtar–değer 67.503 istekte. Alt tablo bunu netleştiriyor: derinlik birden üçe çıktığında belge ve geniş sütunun istek sayısı 1, 2, 3 diye ilerliyor, çizgeninki 1’de kalıyor. Çizge deposunun kazandığı şey dokunulan kayıt değil, istek sayısının dolaşma derinliğinden bağımsız olmasıdır. Derinliği önceden bilinmeyen bir dolaşma, başka türlerde kaç istek edeceği bilinmeyen bir işe dönüşür.
Hesaba Geri Dönüş
Sorgu sayıları K01’in satırlarına doğrudan çevrilir: her örüntünün istek hızı, o örüntünün gerektirdiği sorgu sayısıyla çarpılır.
// tur/hesap.mjs — olculen sorgu ve kayit sayilarinin K01 hesabindaki satirlara etkisi. // Hepsi aritmetiktir; girdi tur/erisim.mjs kosumunun ciktisidir. const OKUMA = 41.67, YAZMA = 97.22, DEPO = 138.89, ORAN = 2.33; // K01 hesaplari const TARAMA = 833.33, SATICI = 4000, PENCERE = 4 * 3600; // K01 hesaplari ve V10 const OLCUM = { // tur/erisim.mjs: [sorgu, kayit] "anahtar-deger": { P1: [1, 1], P2: [2, 2], P3: [3001, 3001] }, "belge": { P1: [1, 1], P2: [1, 1], P3: [1, 3000] }, "genis sutun": { P1: [1, 3], P2: [2, 2], P3: [1, 3000] }, "cizge": { P1: [1, 4], P2: [1, 2], P3: [1, 6001] }, }; console.log(`${"depo turu".padEnd(16)}${"depoya ulasan/s".padStart(16)}${"kat".padStart(7)}` + `${"yazma/okuma".padStart(13)}${"toplu istek/s".padStart(15)}${"toplu kayit/s".padStart(15)}` + `${"kat".padStart(7)}`); for (const [tur, o] of Object.entries(OLCUM)) { const okuma = OKUMA * o.P1[0], yazma = YAZMA * o.P2[0]; const istek = (o.P3[0] * SATICI) / PENCERE, kayit = (o.P3[1] * SATICI) / PENCERE; console.log(tur.padEnd(16) + (okuma + yazma).toFixed(2).padStart(16) + ((okuma + yazma) / DEPO).toFixed(2).padStart(7) + (yazma / okuma).toFixed(2).padStart(13) + istek.toFixed(2).padStart(15) + kayit.toFixed(2).padStart(15) + (kayit / TARAMA).toFixed(2).padStart(7)); } console.log(`K01 tabani: depoya ulasan ${DEPO} istek/s, yazma/okuma ${ORAN}, ` + `toplu tarama ${TARAMA} kayit/s (toplu istek/s tek depoda ${(SATICI / PENCERE).toFixed(2)})`);
depo turu depoya ulasan/s kat yazma/okuma toplu istek/s toplu kayit/s kat anahtar-deger 236.11 1.70 4.67 833.61 833.61 1.00 belge 138.89 1.00 2.33 0.28 833.33 1.00 genis sutun 236.11 1.70 4.67 0.28 833.33 1.00 cizge 138.89 1.00 2.33 0.28 1666.94 2.00 K01 tabani: depoya ulasan 138.89 istek/s, yazma/okuma 2.33, toplu tarama 833.33 kayit/s (toplu istek/s tek depoda 0.28)
İki tür depoya ulasan istek/s satırını 138,89’dan 236,11’e, 1,70 kat büyütüyor ve
depoda yazma/okuma oraninı 2,33’ten 4,67’ye çıkarıyor. İkisinde de neden aynı: P2 iki istek
istiyor. Aynı aritmetiği bu konunun ilk dersi de üretmişti — okuma yolunu ayırmak için yazma
yolunu iki hedefe uzatmak aynı 236,11’i veriyordu. Depo türü değişse de sayı aynı yerden
geliyor: yazma yolunun kaç hedefe dokunduğundan.
Toplu iş sütunu asıl uyarıyı taşıyor. Belge ve geniş sütun deposunda gün sonu işi dört saatlik pencerede saniyede 0,28 istek üretiyor ve K01’in hesapladığı 833,33 kayıt/s taramayı birebir tekrarlıyor. Anahtar–değer deposunda aynı iş saniyede 833,61 istek üretiyor: dört saatlik bir pencerede depo, bütün günün adım adım kurulan 138,89 istek/s’sinin altı katını görüyor. Bu, tek bir mimari kararın K01’in en büyük kalemini hangi sütuna taşıdığını gösteriyor — kayıt sayısı değişmedi, istek sayısı değişti.
Çizge sütunu bunun aynadaki görüntüsüdür. İstek sayısı en iyi türlerle aynı, ama toplu iş kayıt hızı 833,33’ten 1666,94’e, iki katına çıkıyor: her gönderiye bir kenar üzerinden ulaşılıyor. Dolaşmayı ucuzlatan yapı, toplamayı pahalılaştırıyor.
Dört sütun birlikte okunduğunda sonuç tek bir cümledir: hiçbir tür dört örüntüyü birden ucuzlatmaz. Seçim en pahalı örüntüye göre yapılır ve ilk dersin kuralı sürer — kayıt sistemi tek kalır, öteki türler ondan türetilen ikincil depolardır.
Özet
- Bir depo türünü yerel olarak yanıtlayabildiği erişim biçimi tanımlar; yanıtlayamadığı her biçim uygulamaya düşer ve uygulamaya düşen her adım ayrı bir depo isteğidir.
- Tekil okuma dört türü ayırt etmiyor: hepsi tek istek, 1 ile 4 arası kayıt.
- Anahtar–değer deposu dönem taramasında 3001 istek istiyor, belge ile geniş sütun 1; dokunulan kayıt aynı mertebede, istek sayısı üç bin kat farklı.
- Çizge deposunun kazancı dokunulan kayıtta değil istek sayısındadır: üç adımlık dolaşmada belge ve geniş sütun 3 istek harcarken çizge 1 istekte kalıyor ve derinlik arttıkça bu sayı değişmiyor.
- K01’e dönüş: anahtar–değer ve geniş sütun
depoya ulasan istek/s‘yi 138,89’dan 236,11’e (1,70 kat) vedepoda yazma/okuma oraninı 2,33’ten 4,67’ye çıkarıyor. - Gün sonu işi anahtar–değer deposunda saniyede 833,61 istek üretiyor, çizge deposunda tarama hızını 833,33’ten 1666,94 kayıt/s’ye çıkarıyor; hiçbir tür dört örüntüyü birden ucuzlatmıyor.
Sonraki Adım
Sekiz ders verinin nerede ve hangi biçimde durduğunu tasarladı: tür erişim örüntüsüne
göre seçildi, veri çoğaltıldı, federe edildi ve parçalandı; parçalar üç ayrı kuralla
yerleştirildi; okuma yolu denormalizasyonla kısaldı ve tekrarlanan toplam önceden hesaplandı.
Kazanç gerçek: dönem taraması 833,33 kayıt/s’den 8,33’e, bir okumanın çektiği ikincil kayıt
9000’den 40’a indi. Kazanmayan tek sayı depoya ulasan istek/s’nin okuma tarafıdır. Bu dersin
en iyi sütununda bile takip sorgusu saniyede 41,67 istekle deponun kapısını çalıyor, çünkü sekiz
dersin hiçbiri okumanın depoya gitmesini engellemedi; hepsi vardığında ne bulacağını
düzenledi. Sonraki konu okumanın deponun önünde karşılanmasını ele alır ve ilk sorusu kopyanın
hangi katmanda duracağıdır.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.