Dobry cache, nie jest zły

Żeby móc rozmawiać o cache'u, czy całych systemach cache'ujących spróbuję na początku wyjaśnić swoimi słowami co to właściwie jest. Pracując codziennie na komputerze, oglądając strony WWW nieświadomie wykorzystujemy wiele takich mechanizmów. Cache w skrócie to zapamiętanie zestawu danych i umieszczenie ich w takim miejscu by przy następnej próbie dostępu, nie sięgać znów do dysku, nie wykorzystywać mocy obliczeniowej CPU kolejny raz, tylko w celu wygenerowania dokładnie tych samych danych.

Idealnym miejscem do przechowywania informacji jest pamięć operacyjna, dostęp do niej jest kilka rzędów wielkości mniejszy niż w przypadku dysku twardego (chyba, że mówimy o dyskach SSD, ale to nie ta epoka). Świetnym przykładem łatwym do zobrazowania jest wykorzystania cache'u w bazach danych. Wykonując kwerendę SQL zwracającą 1,000,000 rekordów które nie są modyfikowane,  lub są modyfikowane rzadko, silnik bazy danych musiałby odczytywać za każdym razem te same informacje z dysku twardego, ale co zrobi baza danych? Baza wie co to cache, dlatego umieszcza ten zestaw informacji w pamięci podręcznej (RAM) i przy następnym takim zapytaniu, najpierw sprawdzi pamięć pod kątem dostępności tych danych i dopiero w przypadku kiedy ich nie będzie, sięgnie do dysku twardego, oszczędzając CPU, pamięć i dysk twardy.

Klasyczna architektura serwisu WWW.

Do wygenerowania dynamicznej strony np. skryptu PHP przez serwer WWW, wymagane jest:

  • odebranie połączenia od użytkowników
  • połączenia się z serwerem bazodanowym
  • odczyt danych z serwera plików (np. poprzez NFS, iSCSI)

Każda z tych operacji jest kosztowna, szczególnie jeśli chodzi o odpytanie bazy danych ciężką kwerendą SQL, dołączmy do tego czas odczytu plików ze zdalnego serwera storage przy każdym requeście i mamy klasyczny przykład architektury podatnej na zakleszczenia, gdzie w przypadku obciążonej bazy danych procesy serwera httpd czekają na odpowiedź i kolejne jeszcze bardziej pogarszają sytuację, analogicznie wolny czas dostępu do serwera plików, może bardzo wpłynąć na obciążenie serwera bazodanowego, ponieważ sesje do bazy będą dłuższe i będzie ich znacznie więcej.

Co można zrobić?

Możemy użyć akceleratora WWW (reverse proxy) i dodatkowo wykorzystać kilka poziomów cache (np. memcache, APC), sam serwer akcelerujący można umieścić na serwerze www lub przed nim, kolejny krok to ustawienie odpowiednich nagłówków HTTP dla strony WWW (Expires, Cache-Control, Pragma), to tylko kilka czynności które możemy wdrożyć, a dzięki nim sam przepływ ruchu może wyglądać następująco:

Jest to bardzo proste zobrazowanie działania mechanizmów cache'ujących, w tym przypadku zastosowany został mechanizm pracujący na warstwie HTTP. Reverse proxy zachowuje całą stronę wygenerowaną przez Apache (PHP) w swojej pamięci i przy następnym odwołaniu, żądanie nie jest nawet przekazywane do Apache, odpowiedź jest podawana z systemu cache. Nagłówki Expires, Cache-Control pomagają nam sterować czasem cache'owania, może to być zarówno kilka minut jak i lat.

Przykłady systemów cache pracujących na niższych warstwach:

memcached - jest daemonem pracującym w systemie, który potrafi przechowywać dane w pamięci RAM na zasadzie klucz => wartość, jest to chyba jeden z najczęściej używanych mechanizmów przy budowaniu wydajnych i wysoko skalowanych platform (nie tylko WWW). Działanie memcached opiera się na zapamiętywaniu danych przez określony przez nas czas pod konkretnym kluczem:

Przykład: umieszczamy dane "jamzed" (6 bajtów) pod kluczem "test", na okres 100 sekund

jamzed@yoursite:~$ telnet 127.0.0.1 11211
Trying 127.0.0.1...
Connected to 127.0.0.1.
Escape character is '^]'.
set test 0 100 6
jamzed
STORED

get test
VALUE test 0 6
jamzed
END

Najczęściej zapamiętywane są wyniki zapytań SQL, w prosty sposób budujemy klucz i pod nim zapisujemy zwrócone informacje z bazy danych, klucz taki możemy budować poprzez wyliczenie sumy MD5 z zapytania ($klucz = md5(SELECT * FROM TB_USERS);). Jeśli wykorzystujemy memcached z poziomu PHP, to istnieje gotowy moduł do wykorzystania.

APC - Alternative PHP Cache, w zasadzie działania niewiele się różni APC od memcached, również zapamiętuje wartości pod konkretnym kluczem, z tą różnicą, że dane są przechowywany w shared memory naszego serwera (lokalnie). Nie ma możliwości współdzielenia elementów pomiędzy różnymi serwerami zapamiętanych przy użyciu APC, każdy zestaw danych musi być indywidualnie zapamiętany na każdej maszynie osobno, co w przypadku klastrów może powodować desynchronizację i utratę spójności zwracanych użytkownikom danych.

Nie sposób jest wymienić wszystkie dostępne rozwiązania, starałem się naświetlić jedynie możliwości tego typu rozwiązań, jeśli ktoś jest zainteresowany bardziej tematem, prosimy o informacje.

Jakie są korzyści i wady korzystania z takich rozwiązań:

+ zwiększenie wydajności platformy
+ zmniejszenie wykorzystania zasób serwera
+ przyśpieszenie pracy całego systemu
+ zmniejszenie kosztów (mniej serwerów, mniejsze koszty)

- należy dbać o czas przechowywania danych (nieaktualne dane powodują problemy)
- kosztowne wdrożenie systemu
-
cache to kolejny element systemu (wydłużenie czasu diagnozy podczas awarii)

Należy przyjąć, że nie istnieją wydajne platformy bez cache'a, nie martwmy się tym, że możemy serwować dane użytkownikom z opóźnieniem kilki minut, przy odpowiednio zgranych parametrach te czasy można niwelować do minimum, ale nawet minimalny czas pozwoli nam uchronić się przed dużymi skokami ruchu (wykop effect).

Pomimo niektórych wad, warto poświęcić czas na dostosowanie konkretnych rozwiązań i uszyć coś na miarę naszych potrzeb, wykorzystanie cache'a z głową przyniesienie na pewno wiele korzyści i moim zdaniem jest to koszt który warto ponieść raz, by w przyszłości móc rozbudowywać i skalować łatwo i szybko nasz system.

A Wy jakie mechanizmy cache stosujecie w swoich systemach?