najnowsze realizacje:
Ivan Leskov
Grzegorzfirlej.pl - v2
Sloniec.com - Magdalena & Rafał Słoniec
Szymon Janicki - strona domowa
Diamond Hair Design Studio

Cache'owanie - czyli jak oszczędzić serwer

Dzisiejsze serwisy w ogromnej mierze opierają swoje działanie o mniej lub bardziej zaawansowanych skryptach PHP i bazie danych. Prawidłowe działanie serwisu wymaga często powtarzania tych samych zadań. Przykłady z życia wzięte, czyli serwis ten i mój www.ericclapton.pl:

  • lista najnowszych artykułów na blogu,
  • treść konkretnego artykułu,
  • tekst utworu,
  • lista dyskografii i dane konkretnego albumu.

Po co więc za każdym razem wyciągać te same dane z bazy danych. Fakt, w tym przypadku zapytania nie będą skąplikowane i nie obciążą znacznie serwera. Ale po co je wykonywać gdy dane nie zmieniają się na bierząco? W przypadku małej ilości gości efekt znikomy ale pomyślmy przez chwilę, że mój (i Twój) blog ma popularność strony głównej Onetu (marzyć przecież można :P ). A przecież ruchu nie generują tylko żywi internauci ale też roboty internetowe.

Jeżeli budujesz serwisy w oparciu o system Smarty cała realizacja cache-owania zajmie Ci dosłownie kilka minut. Wystarczy w kodzie umieścić kilka linijek i po kłopocie :) Oto przykład zastosowania tego mechanizmu:

$my_cache_id='blog|art|'.$dane_z_bazy['id_artykulu'];
$smarty->caching = 2;
$smarty->cache_lifetime = 3600;
$smarty->display("index.tpl", $my_cache_id);

Opis kodu:

  1. Ustalamy ID cache'a. W tym przypadku zastosowałem obsługę grup cache'y. Rozdzielane znakiem | kategorie można dla ułatwienia porównać do katalogów na dysku. Mamy więc /blog/art/$id_artykulu. Po co tak skąplikowanie a nie od razu a nie np. 'blog-'.$id_artykulu? O tym za chwilę.
  2. Ustawiamy manualne zarządzanie czasem trwania ważności cache'a.
  3. Podajemy w sekundach czas ważności cache'a. W skrócie - co tyle sekund system od nowa generuje stronę korzystając z bazy danych i całości kodu PHP.
  4. Metoda display() doposażona w ID cache'a. Dzięki niemu Smarty wie, którego cahce'a użyć.

 

Podobnie gdy tworzymy listę najnowszych wpisów. Oznaczamy tylko inaczej cache, na przykład tak:

$my_cache_id='blog|lista';

 

A co gdy aktualizujemy wpis a cache mamy utworzony na tydzien (Poważnie, po co orać bazę bez sensu? Gdy coś się nie zmienia taki czas ważności cache'a nie powinien być czymś niezwykłym)? Wystarczy jedna linia w funcji zapisu. Wyczyści ona cache odnawianego artykułu. Identyczna linia powinna być umieszczona w funkcji moderacji komentarzy.

$smarty->clear_cache(null,'blog|art'.$dane_z_bazy['id_artykulu']);

 

A co gdy blog posiada linki wewnętrzne (np. następny/poprzedni) i pojawienie się nowego wpisu wymusza wiele zmian? Oto rozwiązanie:

$smarty->clear_cache(null,'blog');

Polecenie sprzątnie wszystkie cache'e bloga. Dzięki temu podczas odwiedzin kolejnych podstron zostaną wygenerowane nowe, aktualne podstrony.

 

Jak jeszcze możemy pomóc? Najnowsze przeglądarki obsługują kompresję kodu algorytmem gzip. Serwer kompresuje wygenerowane (także za pomocą cache'y) źródło i wysyła je do przeglądarki, która w tle, przed wyświetleniem wypakuje je samodzielnie. W tym momencie zaoszczędzamy, co oczywiste, transfer serwera. Spakowane dane HTML zajmują kilka razy mniejszą objętość. Uruchomienie kompresji jest jeszcze prostsze od obsługi cache'y.

if (!ini_get ('zlib.output_compression')) {
  if (substr_count ($_SERVER['HTTP_ACCEPT_ENCODING'], 'gzip')) {
    ini_set ('zlib.output_compression_level', 9);
    ob_start ('ob_gzhandler');
  }
}

Opis kodu:

  1. Sprawdzamy czy kompresja jest na serwerze domyślnie włączona...
  2. ... jeśli nie sprawdzamy czy przeglądarka akceptuje dane gzip...
  3. jeśli tak ustawiamy poziom kompresji na 9 (można podać [1..9]) ...
  4. i uruchamiamy kompresję.

Linie te muszą znajdować się przed wysłaniem do przeglądarki jakichkolwiek danych, także przed ueuchomieniem sesji. Najprościej umieścić je na początku skryptu, zazwyczaj takie rozwiązanie zadziała prawidłowo. Zysk jest widoczny - czas pracy serwera potrzebny na kompresję jest niższy niż różnica przesłania danych skompresowanych i niekompresowanych.

Tyle na razie. W kolejnych wpisach postaram się pokazać jak można oszczędzić serwer w przypadku konieczności częstego powtarzania skąplikowanych obliczeń na dużej ilości danych.