Çok temel bir cache sistemi örneği verelim o halde.
Bu örnekte, herhangi bir kaynaktan çeşitli işlemlerden geçirilip sunulan verilerin, her sayfa çağrılışında işlem yapmak yerine, kayıt ederek, kayıttan kullanıcıya sunulmasını hedefliyoruz.
Örnekte düz kod olarak örnek vereceğim, kendi uygulamanıza göre fonksiyon yazıp nerede/nasıl uygulayacağınız kendi tasarrufunuzda. Örnekte yer alan kullanıcı girdilerini güvenlik gereği filtrelemek hususlarına da değinmeyeceğim.
1. Adım: Cache dosyasını ve süresini tanımlamak.
Örnek:
PHP:
$cachefile = 'cache/benimsayfam.dat';
$cachetime = 60 * 60 * 24 // 24 saat
Tabii her sayfa için örnekteki gibi bir isim belirtmek zor olacaktır. Bir kategori sayfanız olduğunu düşünelim. Bu kategori sayfası URL'den kategori ID'sini alıyor olsun. (kategori.php?katid=12 gibi)
PHP:
$cachefile = 'cache/kategori-'.$_GET['katid'].'.dat';
$cachetime = 60 * 60 * 24 // 24 saat
Burada her bir kategori icin cache dosyasi tanimlamis olacak script. Cagirdiniz kategori 25 ise, kategori-25.dat dosyasını tanımlamış olduk script içinde.
24 saatlik bir cache süresi tanımladık. Herhangi bir zorunlu hal olmadığı takdirde, 24 saat sürece cache'den dosya okutulacak, database'den değil. Bu isteğinize, veri yapınızın güncellenme süresine göre değişebilir.
2. Adım: Cache dosyasını kontrol etmek
PHP:
if (file_exists($cachefile)) {
// cache dosyasi mevcut.
$mtime = filemtime($cachefile); // dosyanın son değiştirilme zamani
if (time() - $cachetime < $mtime) {
// dosya cache suresi limitinden daha once degistirildiyse uygun
$data = file_get_contents($cachefile);
echo $data;
echo '<!-- Cached kopya. Yaratilis zamani: '.date ("F d Y H:i:s.", $mtime).' -->';
exit;
}
}
Bu script, oncelikle dosyanın varlığını kontrol edecek, daha sonra son değiştirilme zamanı belirttiğimiz cache süresinden sonra ise, dosyayı kayıttan okuyup scriptin geri kalan kismini (database islemleri vb) bu kullanici icin tekrar tekrar calistirmayacaktir.
3. Adım: Cache dosyasının hazırlanması
Eğer sistemde oluşmuş bir cache kayıt yoksa, tabi doğal olarak yukarıdaki kod daki if döngüsü yanlış sonuçlanıp, sizin scriptinizin normal işleyiş kısmına geçecektir. Yapmamız gereken bu adımlar bittikten sonra kayıt altına alınmasını istediğimiz kısımın en sonuna cache dosyasını hazırlatmaktır. Veriyi kaydetmek için kayıt altına alınacak kısmın en tepesine de çıktı kontrol fonksiyonlarından birini yerleştiriyoruz.
PHP:
ob_start(); //output buffer i aciyoruz, kayit icin
// normal islemleriniz
// script ve database okumalari vs..
// kayit altina alinacak kismin sonuna gelindiginde
$icerik = ob_get_contents(); // ob_start() ile basladigimiz alandan bu ana kadar ki ciktiyi degiskene aldik.
ob_end_flush(); // sifonu cek. ciktiyi ekrana bosalt.
if ($cachefile) {
$f = fopen($cachefile,'w+'); // dosyayı yazmak icin ac, icerigi sifirla.
fwrite($f,$icerik);
fclose($f);
// dosyaya kayit ettigimiz icerigi yazdik.
}
Icerik yazilip dosya kapatilinca (cache dizininin chmod ayarı yazılabilir olmalı), tepede belirttigimiz cache dosyasi kaydedilmis oldu, bir sonraki refresh'te db islemleri yapilmayacak, dosya kayittan okunup kullaniciya sunulacaktir.
Birlestirince
PHP:
// script basi islemleriniz
// kayit edilmesini istediginiz alanin basi.
$cachefile = 'cache/kategori-'.$_GET['id'].'.dat';
$cachetime = 60 * 60 * 24 // 24 saat
if (file_exists($cachefile)) {
// cache dosyasi mevcut.
$mtime = filemtime($cachefile); // dosyanın son değiştirilme zamani
if (time() - $cachetime < $mtime) {
// dosya cache suresi limitinden daha once degistirildiyse uygun
$data = file_get_contents($cachefile);
echo $data;
echo '<!-- Cached kopya. Yaratilis zamani: '.date ("F d Y H:i:s.", $mtime).' -->';
exit;
}
}
ob_start();
// DATABASE ISLEMLERI VB. KAYIT ALTINA ALINACAK ISLEMLER..
$icerik = ob_get_contents(); // ob_start() ile basladigimiz alandan bu ana kadar ki ciktiyi degiskene aldik.
ob_end_flush(); // sifonu cek. ciktiyi ekrana bosalt.
if ($cachefile) {
$f = fopen($cachefile,'w+'); // dosyayı yazmak icin ac, icerigi sifirla.
fwrite($f,$icerik);
fclose($f);
// dosyaya kayit ettigimiz icerigi yazdik.
}
// kayit edilmesini istediginiz alanin sonu
// script sonu islemleriniz
Olasi sorun: 24 saat olarak tarif edilmis bir cache dosyasinda bazen kullanicilar icerige mudahale ediyorlar, bu durumda cache eskimis olacak, kullanici belki de yaptigi update islemini goremeyecek. Ornegin scriptinizde yorumlar kismi var, cache'e almak istediniz. Ancak bir kisi yeni yorum postaladi. Cache'den okursa goremeyecek yeni yorumu.
Bu gibi durumlar icin farkli cozumler kullanilabilir. Update islemini yapan script parcacigina, update yapildigina dair bir session bilgisi atabiliriz.
PHP:
// cache dosyasindaki verilere direk mudahele edecek
// kullanici islemleri
// update vs..
$_SESSION['forcecache']['bolge'] = 1;
"Bolge" isimli alanin cache bilgisini update edin diye session bilgisi atadik. Dinamik olarak bolge adini degistirebilirsiniz.
Daha sonra cache dosyasinin kontrol edildigi bolgeye bir sart daha ekleriz. Bolge alani forcecache olarak tanimlanmis mi diye. Bu durumda bolgeye 1 atadiginiz icin if sarti false donecek ve script cache'i okuyamayacak, mecburen db islemleri yapilip, cache script sonunda yeniden fwrite() ile guncellenecektir.
PHP:
if (file_exists($cachefile) AND $_SESSION['forcecache']['bolge'] != '1') {
//
}
Bunu yaptiginizda fwrite() isleminden sonra SESSION bilgisini tekrar sifirlayiniz. Aksi takdirde, session bilgisi sayesinde her refresh'de cache'i force eder.
PHP:
fwrite($f,$icerik);
fclose($f);
unset($_SESSION['forcecache']['bolge']); // cache'i bir kere guncelledik, cache zorlama bilgisini kapat.
Ek olarak Unix dosya sisteminde diskte, bellekten sanal bir alan yaratıp disk olarak mount edebiliyorsunuz. Bu durumda cache dosyalarını isterseniz bellekte de tutma şansınız var. Okuma/yazma hızı fark ediyor.
100K hitli bir e-store'da çok basit bu tarz bir uygulama kullanarak, Apache'ye gelen hitler saniye başı ortalama 600 iken, SQL server'a gelen hiti saniye başı 10'lara kadar düşürmüştük. Server load'unda muazzam bir farkedilirlik ortaya çıktı.
Kaynak gostererek yayinlayabilirsiniz.