Ders 17 / 21
Önbellekleme
Anahtar tabanlı önbellek, yaşam süresi, hatirla fonksiyonu ve geçersiz kılma; isabette hesaplamanın atlanması, geçersiz kılmadan sonra koşması; yaşam süresi dolunca yeniden hesaplama ve geçersiz kılma unutulursa güncel kaydın yanıta bağlanmaması.
Yayın Güncelleme
Bu derste
- Anahtar, yaşam süresi ve geçersiz kılmayı birbirinden ayırır
- Önbellek isabetinde hesaplamanın atlandığını, geçersiz kılmadan sonra koştuğunu ölçer
- Geçersiz kılma unutulduğunda yanıtın bayat değer taşıdığını ve yaşam süresinin bunu ne zaman düzelttiğini gösterir
İçindekiler
Bildirimler dersinde bildirim kanalları her istekte yeniden çalıştı; her istek aynı soruyu baştan sormak zorundaydı. Bir değerin hesaplanması pahalıysa ve değer sık sık değişmiyorsa, sonucu saklamak ve ikinci istekte hesaplamayı atlamak daha ucuzdur. Bu ders şunu sorar: kayıt değiştiğinde saklanan değer de değişir mi?
Önbellek (cache) bir değeri bir anahtar altında saklayan yerdir. Yaşam süresi (TTL), saklanan değerin ne kadar geçerli olduğunu söyler; süre dolunca değer bayattır ve yeniden hesaplanır. Dersin merkezindeki fonksiyon hatirla(anahtar, sure, hesapla)’dır: anahtar önbellekte geçerliyse değeri döndürür ve hesapla fonksiyonunu çağırmaz (isabet); değilse çağırır, sonucu saklar ve döndürür. Kayıt değiştiğinde saklanan değeri silmeye geçersiz kılma (invalidation) denir. Önbellek hızı verir, ama bayatlık riskini de beraberinde getirir; ve risk, geçersiz kılmanın unutulduğu yerde somutlaşır.
US5. Önbellek bir dizidir ve bir sunucu, bir ağ ya da bir dosya değildir. Saat bir değişkendir ($simdi); sleep çağrılmaz ve süre geçişi bu değişkeni artırarak taklit edilir, böylece ölçüm gerçek zamandan bağımsızdır. Yaşam süresi tam sınırda dolmuş sayılır: bitiş anı geldiğinde kayıt artık geçerli değildir. Önbellek tutarlılığı, aynı anda gelen iki isteğin birbirini ezmesi ve boyut sınırı modellenmez.
Ölçüm Çekirdeği
Kurs boyunca her derste aynı ölçüm kullanılır. kostu.php beklenen bir parçanın bu istekte koşup koşmadığını kaydeder; dosya her derste birebir verilir ve çıktı bloğu yoktur, yalnız diske yazılır. Gunluk üç liste tutar: isteğin yoluna konanlar, istek içinde çağrılanlar ve isteğin dışına bırakılanlar. sinifla() bunlara sırayla bakar. Karar sırası sinifla()’nın kendisidir: koştu, ertelendi, atlandı, bağlanmadı. Parçayı geliştirici adlandırır; ölçülen, geliştiricinin bu istekte koşmasını beklediği parçalardır.
<?php // kostu.php — beklenen bir parça bu istekte koştu mu? declare(strict_types=1); final class Gunluk { public static array $yol = []; // isteğin yoluna konan parçalar public static array $kostu = []; // istek içinde çağrılanlar public static array $ertelenen = []; // isteğin dışına bırakılanlar public static function yeniIstek(): void { self::$yol = self::$kostu = self::$ertelenen = []; } public static function yola(string $parca): void { self::$yol[] = $parca; } public static function kostu(string $parca): void { self::$kostu[] = $parca; } public static function ertele(string $parca): void { self::$ertelenen[] = $parca; } } function sinifla(string $parca): string { return match (true) { in_array($parca, Gunluk::$kostu, true) => 'koştu', in_array($parca, Gunluk::$ertelenen, true) => 'ertelendi', in_array($parca, Gunluk::$yol, true) => 'atlandı', default => 'bağlanmadı', }; }
Bu derste ölçülen parça hesapla’dır, yani önbelleğe verilen hesaplama fonksiyonu. hatirla() her çağrıda onu yola koyar; isabet olursa fonksiyon çağrılmaz ve parça atlandı sınıfında kalır, isabet olmazsa kostu() ile işaretlenir. İkinci bir parça, guncel, son bölümde yanıtın depodaki güncel kaydı taşıyıp taşımadığını ölçer.
Önbellek, Yaşam Süresi ve Geçersiz Kılma
onbellek.php dilimin tamamıdır. Depo kalıcı kaydın küçültülmüş biçimidir: yazı kimliğinden başlığa bir dizi ve gerçeğin kaynağı odur. Onbellek anahtar başına bir değer ve bir bitiş anı tutar. hatirla() hesaplamayı yola koyar, girdiye bakar, bitiş anı şimdiden büyükse değeri döndürür; değilse fonksiyonu çağırır, değeri simdi + sure bitişiyle saklar. unut() girdiyi siler, yani geçersiz kılar. YazilarDenetleyicisi::goster() başlığı hatirla() ile alır ve kaç kez gerçekten hesaplandığını sayar. guncelMi() ölçüm yardımcısıdır: yanıttaki başlık depodakiyle aynıysa guncel parçasını yola koyar ve çağırır; aynı değilse hiçbir şey yapmaz.
<?php // onbellek.php — anahtar tabanlı önbellek, yaşam süresi, geçersiz kılma declare(strict_types=1); require_once 'kostu.php'; final class Depo { // kalıcı kayıt: gerçeğin kaynağı public static array $baslik = [7 => 'Eski baslik']; } final class Onbellek { public int $simdi = 0; // saat bir değişkendir; sleep çağrılmaz private array $girdiler = []; // anahtar => [değer, bitiş] public function hatirla(string $anahtar, int $sure, callable $hesapla): mixed { Gunluk::yola('hesapla'); $girdi = $this->girdiler[$anahtar] ?? null; if ($girdi !== null && $girdi[1] > $this->simdi) { return $girdi[0]; // isabet: hesaplama çağrılmaz } Gunluk::kostu('hesapla'); $deger = $hesapla(); $this->girdiler[$anahtar] = [$deger, $this->simdi + $sure]; return $deger; } public function unut(string $anahtar): void { // geçersiz kılma unset($this->girdiler[$anahtar]); } } final class YazilarDenetleyicisi { public static int $hesaplama = 0; public function __construct(private Onbellek $onbellek) {} public function goster(int $id): array { Gunluk::yeniIstek(); Gunluk::yola('goster'); Gunluk::kostu('goster'); $baslik = $this->onbellek->hatirla("yazi.$id", 60, function () use ($id) { self::$hesaplama++; return Depo::$baslik[$id]; }); return [200, $baslik]; } } function guncelMi(array $yanit, int $id): void { // yanıt, depodaki güncel kaydı taşıyor mu if ($yanit[1] === Depo::$baslik[$id]) { Gunluk::yola('guncel'); Gunluk::kostu('guncel'); } }
İsabette Hesaplama Atlandı
İlk ölçüm üç istek yapar. Birincisi önbellek boştur. İkincisi on birim sonra gelir, hâlâ süre içindedir. Üçüncüsü yazının başlığı depoda değiştirilip önbellekteki girdi geçersiz kılındıktan sonra gelir.
<?php // isabet.php — ilk istek, isabet, geçersiz kılma require_once 'onbellek.php'; $onbellek = new Onbellek(); $denetleyici = new YazilarDenetleyicisi($onbellek); function istek(string $etiket, YazilarDenetleyicisi $d, Onbellek $o): void { $yanit = $d->goster(7); printf("%s (saat %d) -> %d %s\n", $etiket, $o->simdi, $yanit[0], $yanit[1]); echo ' hesapla: ', sinifla('hesapla'), ', toplam hesaplama: ', YazilarDenetleyicisi::$hesaplama, "\n"; } istek('ilk istek', $denetleyici, $onbellek); $onbellek->simdi = 10; istek('ikinci istek', $denetleyici, $onbellek); Depo::$baslik[7] = 'Yeni baslik'; $onbellek->unut('yazi.7'); $onbellek->simdi = 20; istek('geçersiz kılmadan sonra', $denetleyici, $onbellek);
ilk istek (saat 0) -> 200 Eski baslik hesapla: koştu, toplam hesaplama: 1 ikinci istek (saat 10) -> 200 Eski baslik hesapla: atlandı, toplam hesaplama: 1 geçersiz kılmadan sonra (saat 20) -> 200 Yeni baslik hesapla: koştu, toplam hesaplama: 2
Bu dersin gözlemi ikinci istekte. Önbellek girdisi geçerliydi, hatirla() değeri döndürdü ve hesapla yola konduğu hâlde çağrılmadı: atlandı. Toplam hesaplama sayısı 1’de kaldı. Yanıt aynı başlığı taşıdı, Eski baslik; istemci farkı görmedi, ama iş yapılmadı. Aynı hesapla ilk istekte koştu: önbellek boştu, hesaplama çağrıldı, sonuç saklandı ve toplam 1 oldu.
Üçüncü istek karşıttır. Başlık depoda değişti ve unut('yazi.7') girdiyi sildi. Yanıt Yeni baslik taşıdı, hesapla yeniden koştu ve toplam hesaplama 2 oldu. Aynı anahtar, aynı denetleyici; hesaplamanın koşup koşmayacağını yalnız girdinin var olup olmadığı belirledi. Geçersiz kılma, önbelleğe değişikliği haber veren tek yoldur.
Sınırlayıcı Ölçüm
Geçersiz kılma unutulursa ne olur ve yaşam süresi bunu düzeltir mi? İkinci ölçüm üç istek yapar. İlki değeri saklar. Ardından depoda başlık değişir ama girdi silinmez. İkinci istek süre dolmadan gelir. Üçüncüsü süre dolunca gelir. Her istekte guncel parçası, yanıtın depodaki güncel kaydı taşıyıp taşımadığını ölçer.
<?php // bayat.php — yaşam süresi dolunca ve geçersiz kılma unutulunca require_once 'onbellek.php'; $onbellek = new Onbellek(); $denetleyici = new YazilarDenetleyicisi($onbellek); function olc(string $etiket, YazilarDenetleyicisi $d, Onbellek $o): void { $yanit = $d->goster(7); guncelMi($yanit, 7); printf("%s (saat %d) -> %s\n", $etiket, $o->simdi, $yanit[1]); echo ' hesapla: ', sinifla('hesapla'), ', güncel kayıt: ', sinifla('guncel'), "\n"; } olc('önbelleğe al', $denetleyici, $onbellek); Depo::$baslik[7] = 'Yeni baslik'; // geçersiz kılma unutuldu $onbellek->simdi = 30; olc('süre dolmadan', $denetleyici, $onbellek); $onbellek->simdi = 60; olc('süre dolunca', $denetleyici, $onbellek); echo 'toplam hesaplama: ', YazilarDenetleyicisi::$hesaplama, "\n";
önbelleğe al (saat 0) -> Eski baslik hesapla: koştu, güncel kayıt: koştu süre dolmadan (saat 30) -> Eski baslik hesapla: atlandı, güncel kayıt: bağlanmadı süre dolunca (saat 60) -> Yeni baslik hesapla: koştu, güncel kayıt: koştu toplam hesaplama: 2
İlk istekte hesapla koştu ve yanıt depodaki kaydı taşıdığı için guncel de koştu. Sonra başlık depoda değişti ve girdi silinmedi. İkinci istekte, saat 30’da, önbellek geçerli girdiyi döndürdü: hesapla atlandı ve yanıt Eski baslik taşıdı, yani depodaki Yeni baslik yanıta ulaşmadı. guncel parçası bu yüzden yola hiç girmedi ve bağlanmadı. Yanıt 200’dür ve hata yoktur; bayat değer sessizce döner. Bu, önbelleğin en tehlikeli hâlidir: yanıt başarılı görünür ve eksik olan hiçbir yerde belirtilmez.
Üçüncü istek saat 60’ta geldi ve yaşam süresi tam dolmuştu. hesapla yeniden koştu, yanıt Yeni baslik taşıdı ve guncel de koştu. Toplam hesaplama sayısı 2’dir. Yaşam süresi bayatlığı düzeltti, ama ancak sürenin sonunda. Süre ne kadar uzunsa unutulan geçersiz kılmanın bedeli o kadar büyüktür. Geçersiz kılma ile yaşam süresi birbirinin yerine geçmez: geçersiz kılma değişikliği hemen yansıtır, yaşam süresi unutulan geçersiz kılmaya karşı bir üst sınırdır.
Özet
- Bu dersin ölçü sınıfı atlandı: ikinci istekte önbellek isabet etti,
hesaplayoldaydı ve çağrılmadı, toplam hesaplama sayısı 1’de kaldı. hatirla(anahtar, sure, hesapla)geçerli girdiyi döndürür, değilse hesaplar ve saklar; geçersiz kılma girdiyi siler.- Karşıt: geçersiz kılmadan sonra
hesaplayeniden koştu, yanıt yeni başlığı taşıdı ve toplam 2 oldu. - Sınırlayıcı ölçümde yaşam süresi dolunca
hesaplayeniden koştu; geçersiz kılma unutulunca güncel kayıt yanıta bağlanmadı ve bayat değer 200 ile döndü. - Yaşam süresi geçersiz kılmanın yerine geçmez; yalnız unutulan geçersiz kılmanın bayatlık süresini sınırlar.
Sonraki Adım
Önbellekteki değer, kaynağındaki değişiklikten haberdar olmadığı sürece bayat kaldı. Peki saklanan şey bir değer değil de bir dosyaysa ve dosyanın yazıldığı disk değişirse ne değişir: yükleme kodu mu, yanıt mı, dosyanın kendisi mi? Dosya Depolama dersi bir dosyayı iki ayrı diske yazar ve okur ve disk değiştiğinde bunlardan hangisinin değiştiğini ölçer.
İlerlemeni kaydetmek ve not almak için Giriş yap
Notlarım
Not almak için giriş yapmalısın.