Nigdy nie pisz swojego rozwiązania do buforowania

Innym sposobem na przyspieszenie pracy z bazą danych jest buforowanie obiektów, o które prosiliśmy już wcześniej.

Ważny! Nigdy nie pisz własnego rozwiązania do buforowania. To zadanie ma tak wiele pułapek, o których nawet nie śniłeś.

Problem 1 — opróżnianie pamięci podręcznej . Czasami występują zdarzenia, gdy obiekt musi zostać usunięty z pamięci podręcznej lub zaktualizowany w pamięci podręcznej. Jedynym sposobem, aby zrobić to kompetentnie, jest przekazanie wszystkich żądań do bazy danych przez silnik pamięci podręcznej. W przeciwnym razie za każdym razem będziesz musiał jawnie powiedzieć pamięci podręcznej, które znajdujące się w niej obiekty mają zostać usunięte lub zaktualizowane.

Problem 2 - brak pamięci . Buforowanie wydaje się świetnym pomysłem, dopóki nie odkryjesz, że obiekty w pamięci zajmują dużo miejsca. Do efektywnego działania pamięci podręcznej aplikacji serwera potrzebne są dodatkowe dziesiątki gigabajtów pamięci.

A ponieważ zawsze brakuje pamięci, potrzebna jest skuteczna strategia usuwania obiektów z pamięci podręcznej. Jest to trochę podobne do modułu wyrzucania elementów bezużytecznych w Javie. A jak pamiętacie, przez dziesięciolecia najtęższe umysły wymyślały różne sposoby znakowania przedmiotów z pokolenia na pokolenie itp.

Problem 3 – różne strategie . Jak pokazuje praktyka, różne strategie przechowywania i aktualizowania w pamięci podręcznej są skuteczne dla różnych obiektów. Wydajny system buforowania nie może stosować tylko jednej strategii dla wszystkich obiektów.

Problem 4 - Efektywne przechowywanie plików . Nie możesz po prostu przechowywać obiektów w pamięci podręcznej. Obiekty zbyt często zawierają odniesienia do innych obiektów itd. W tym tempie nie będziesz potrzebować modułu wyrzucania elementów bezużytecznych: po prostu nie będzie miał czego usuwać.

Dlatego zamiast przechowywania samych obiektów, czasami znacznie wydajniejsze jest przechowywanie wartości ich prymitywnych pól. I systemy do szybkiego konstruowania obiektów na ich podstawie.

W rezultacie otrzymasz w pamięci cały wirtualny DBMS, który powinien działać szybko i zużywać mało pamięci.

Buforowanie bazy danych

Oprócz buforowania bezpośrednio w programie Java, buforowanie jest często organizowane bezpośrednio w bazie danych.

Istnieją cztery duże podejścia:

Pierwszym podejściem jest denormalizacja bazy danych . Serwer SQL przechowuje dane w pamięci inaczej niż w tabelach.

Kiedy dane są przechowywane na dysku w tabelach, bardzo często programiści starają się w jak największym stopniu unikać powielania danych - proces ten nazywa się normalizacją bazy danych. Tak więc, aby przyspieszyć pracę z danymi w pamięci, wykonywany jest proces odwrotny - denormalizacja bazy danych. Kilka powiązanych tabel można już przechowywać w połączonej formie - w postaci ogromnych tabel itp.

Drugie podejście to buforowanie zapytań . I wyniki zapytania.

DBMS widzi, że bardzo często przychodzą do niego takie same lub podobne żądania. Następnie po prostu zaczyna buforować te żądania i ich odpowiedzi. Ale jednocześnie musisz upewnić się, że wiersze, które uległy zmianie w bazie danych, są usuwane z pamięci podręcznej w odpowiednim czasie.

Takie podejście może być bardzo skuteczne w przypadku człowieka, który może analizować zapytania i pomóc DBMS dowiedzieć się, jak najlepiej je buforować.

Trzecie podejście to baza danych w pamięci .

Inne powszechnie stosowane podejście. Pomiędzy serwerem a DBMS umieszczona jest kolejna baza danych, która przechowuje wszystkie swoje dane tylko w pamięci. Jest również nazywany In-Memory-DB. Jeśli masz wiele różnych serwerów uzyskujących dostęp do tej samej bazy danych, za pomocą In-Memory-DB możesz zorganizować buforowanie na podstawie typu konkretnego serwera.

Przykład:

Podejście 4 — klaster bazy danych . Kilka baz tylko do odczytu.

Innym rozwiązaniem jest użycie klastra: kilka DBMS tego samego typu zawiera identyczne dane. Jednocześnie możesz odczytywać dane ze wszystkich baz danych, a zapisywać tylko do jednej. Który jest następnie synchronizowany z resztą baz danych.

To bardzo dobre rozwiązanie, ponieważ jest łatwe w konfiguracji i sprawdza się w praktyce. Zwykle na jedno żądanie do bazy danych o zmianę danych przychodzi do niej 10-100 żądań odczytu danych.

Rodzaje buforowania w Hibernate

Hibernate obsługuje trzy poziomy buforowania:

  • Buforowanie na poziomie sesji (Session)
  • Buforowanie na poziomie SessionFactory
  • Żądania buforowania (i ich wyniki)

Możesz spróbować przedstawić ten system w postaci takiej figury:

Najprostszy rodzaj buforowania (zwany także buforowaniem pierwszego poziomu ) jest realizowany na poziomie sesji Hibernate. Hibernacja zawsze domyślnie korzysta z tej pamięci podręcznej i nie można jej wyłączyć .

Rozważmy od razu następujący przykład:

Employee director1 = session.get(Employee.class, 4);
Employee director2 = session.get(Employee.class, 4);

assertTrue(director1 == director2);

Mogłoby się wydawać, że zostaną tutaj wykonane dwa zapytania do bazy danych, ale tak nie jest. Po pierwszym zapytaniu do bazy obiekt Pracownik zostanie zbuforowany. A jeśli ponownie wyślesz zapytanie do obiektu w tej samej sesji, Hibernate zwróci ten sam obiekt Java.

Ten sam obiekt oznacza, że ​​nawet odwołania do obiektów będą identyczne. To naprawdę ten sam obiekt.

Metody save() , update() , saveOrUpdate() , load() , get() , list() , iterate() i scroll() będą zawsze używać pamięci podręcznej pierwszego poziomu. Właściwie nie ma nic więcej do dodania.

Buforowanie drugiego poziomu

Jeśli pamięć podręczna pierwszego poziomu jest powiązana z obiektem sesji, pamięć podręczna drugiego poziomu jest powiązana z obiektem sesji.Fabryka sesji. Co oznacza, że ​​widoczność obiektów w tej pamięci podręcznej jest znacznie szersza niż w pamięci podręcznej pierwszego poziomu.

Przykład:

Session session = factory.openSession();
Employee director1 = session.get(Employee.class, 4);
session.close();

Session session = factory.openSession();
Employee director2 = session.get(Employee.class, 4);
session.close();

assertTrue(director1 != director2);
assertTrue(director1.equals(director2));

W tym przykładzie do bazy danych zostaną wykonane dwa zapytania. Hibernate zwróci identyczne obiekty, ale nie będzie to ten sam obiekt - będą miały różne referencje.

Buforowanie drugiego poziomu jest domyślnie wyłączone . Mamy więc dwa zapytania do bazy danych zamiast jednego.

Aby go włączyć, musisz napisać następujące wiersze w pliku hibernate.cfg.xml:

<property name="hibernate.cache.provider_class" value="net.sf.ehcache.hibernate.SingletEhCacheProvider"/>
<property name="hibernate.cache.use_second_level_cache" value="true"/>

Po włączeniu buforowania drugiego poziomu zachowanie Hibernacji nieco się zmieni:

Session session = factory.openSession();
Employee director1 = session.get(Employee.class, 4);
session.close();

Session session = factory.openSession();
Employee director2 = session.get(Employee.class, 4);
session.close();

assertTrue(director1 == director2);

Dopiero po tych wszystkich manipulacjach zostanie włączona pamięć podręczna drugiego poziomu, aw powyższym przykładzie zostanie wykonane tylko jedno zapytanie do bazy danych.