Pytanie o poziomy cache Hibernate to jedno z najczęściej pojawiających się pytań rekrutacyjnych. Spotkałem się z tym wiele razy – jeśli chcesz kogoś docisnąć z Hibernate to najlepiej zapytać o cache.
Jednak rekrutacja to nie jedyny powód, by znać temat 🙂 Wiedza ta jest potrzebna także do zrozumienia jak w istocie działa ORM.
Przede wszystkim po co nam ten cache?
Cache Hibernate tak samo jak inne mechanizmy pamięci podręcznej ma na celu zwiększenie wydajności. Jednym z największych problemów systemu ORM jest to, że w gruncie rzeczy działa on na podstawie zapytań SQL do relacyjnej bazy. ORM musi przetłumaczyć dane z tabel na model obiektowy. Naiwna implementacja tego schematu byłaby koszmarnie niewydajna, ponieważ doprowadziłaby do ogromnej ilości zapytań SQL.
Pierwszy poziom (L1) – sposób na zbyt częsty round-trip do bazy danych
L1 cache w Hibernate jest zawsze włączony i nie da się go wyłączyć poprzez konfigurację. Jest on główną warstwą pamięci podręcznej podczas wykonywania danej sesji. Dzięki temu obiekty nie muszą być za każdym razem ściągane z bazy.

Jak widać na powyższym schemacie, sesja przechowuje wyciągane obiekty i nie wysyła SQL do bazy jeśli dany obiekt jest już obecny. Ma to szczególnie duży wpływ w przypadku skomplikowanych operacji, które mogą w wielu miejscach wykorzystywać ten sam obiekt. Po zakończeniu sesji wszystkie obiekty w cache są kasowane.
Schemat użycia w tym wypadku wygląda w taki sposób:
- użytkownik wysyła zapytanie o konkretny obiekt do sesji,
- jeśli obiekt jest w L1, użytkownik dostaje go natychmiast z cache.
Jeśli natomiast obiektu nie ma w L1:
- sesja wysyła zapytanie SQL do bazy,
- obiekt jest umieszczany w L1,
- użytkownik dostaje nowo utworzony obiekt z L1.
Jak widać L1 jest integralną częścią cyklu pracy ORM, co wyjaśnia czemu nie da się go wyłączyć w Hibernate.
Drugi poziom (L2) – cache obiektów pomiędzy sesjami
L2 cache działa na poziomie fabryki sesji (SessionFactory), dlatego jest wspólny dla wszystkich stworzonych sesji. Jeśli jest włączony, to on odpowiada za dostarczanie obiektów do L1. Schemat jego działania można przedstawić na prostym schemacie:

W przypadku włącznego L2, schemat działania jest następujący:
- L1 działa jak zawsze i jeśli obiekt jest w L1, L2 nie jest używany,
- jeśli obiektu nie ma w L1, odpytywany jest L2,
- jeśli obiekt jest w L2, to L2 przekazuje obiekt do L1,
- jeśli obiektu nie ma w L2, to wysyłane jest zapytanie SQL.
Problemy związane z L2
Drugi poziom cache wiąże się ze zwiększeniem wydajności, ale także wprowadza dodatkowe problemy:
- wymagane jest dostarczenie dodatkowej biblioteki zajmującej się implementacją cache L2,
- potrzebna jest dodatkowa konfiguracja Hibernate, strategi cachowania obiektów oraz strategi odświeżania cache,
- L2 potrafi drastycznie zwiększyć ilość zajmowanej pamięci RAM, szczególnie w przypadku nieoptymalnej konfiguracji,
- L2 może zwracać stare kopie obiektów, może być to spowodowanie m.in używaniem zapytań natywnych,
- przy używaniu L2 musimy pamiętać o innych klientach mogących modyfikować bazę.
Podsumowując:
Mam nadzieję, że zawarte informacje wystarczą do zrozumienia jakie zadanie spełniają L1 i L2. Temat cache w Hibernate jest zbyt obszerny by zająć się nim w jednym poście i każdy podpunkt może być rozwinięty w oddzielny artykuł. Z pewnością nie jest to koniec tego tematu na moim blogu 🙂