PostgreSQL
 sql >> Baza danych >  >> RDS >> PostgreSQL

Spring + Hibernate:Wykorzystanie pamięci podręcznej planu zapytań

Trafiłem też na ten problem. Zasadniczo sprowadza się to do posiadania zmiennej liczby wartości w klauzuli IN i Hibernate próbuje buforować te plany zapytań.

Istnieją dwa świetne posty na blogu na ten temat.Pierwszy:

Używanie Hibernate 4.2 i MySQL w projekcie z zapytaniem w klauzuli, takim jak:select t from Thing t where t.id in (?)

Hibernate buforuje te przeanalizowane zapytania HQL. W szczególności HibernateSessionFactoryImpl ma QueryPlanCache z queryPlanCache iparameterMetadataCache . Okazało się to jednak problemem, gdy liczba parametrów w klauzuli jest duża i różni się.

Te pamięci podręczne rosną dla każdego odrębnego zapytania. Więc to zapytanie z 6000parameters to nie to samo co 6001.

Zapytanie w klauzuli jest rozszerzane do liczby parametrów w kolekcji. Metadane są zawarte w planie zapytania dla każdego parametru w zapytaniu, w tym wygenerowanej nazwy, takiej jak x10_, x11_ itd.

Wyobraź sobie 4000 różnych wariacji w liczbie zliczeń parametrów w klauzuli, z których każda ma średnią 4000 parametrów. Metadane zapytania dla każdego parametru szybko sumują się w pamięci, zapełniając stertę, ponieważ nie mogą być zbierane śmieci.

Trwa to do momentu, gdy wszystkie różne warianty licznika parametrów zapytania zostaną zbuforowane lub w maszynie JVM zabraknie pamięci sterty i zacznie się rzucaniejava.lang.OutOfMemoryError:przestrzeń sterty Java.

Opcją jest unikanie klauzul wewnętrznych, jak również używanie stałego rozmiaru kolekcji dla parametru (lub przynajmniej mniejszego rozmiaru).

Aby skonfigurować maksymalny rozmiar pamięci podręcznej planu zapytania, zobacz właściwośćhibernate.query.plan_cache_max_size , domyślnie 2048 (łatwe narzędzie dla zapytań z wieloma parametrami).

A po drugie (również odwołuje się od pierwszego):

Hibernate wewnętrznie używa pamięci podręcznej, która mapuje instrukcje HQL (asstrings) do planów zapytań. Pamięć podręczna składa się z ograniczonej mapy ograniczonej domyślnie do 2048 elementów (konfigurowalnych). Wszystkie zapytania HQL są ładowane przez tę pamięć podręczną. W przypadku nietrafienia wpis jest automatycznie dodawany do pamięci podręcznej. To sprawia, że ​​jest bardzo podatny na thrashing - scenariusz, w którym ciągle umieszczamy nowe wpisy w pamięci podręcznej bez ich ponownego użycia, co uniemożliwia buforowi przyniesienie jakiegokolwiek wzrostu wydajności (dokłada to nawet trochę narzutu na zarządzanie pamięcią podręczną). Co gorsza, ciężko jest przypadkowo wykryć taką sytuację - musisz wyraźnie sprofilować pamięć podręczną, aby zauważyć, że masz tam problem. Powiem kilka słów o tym, jak można to zrobić później.

Tak więc thrashowanie pamięci podręcznej wynika z generowania nowych zapytań z wysokimi szybkościami. Może to być spowodowane wieloma problemami. Dwa najczęściej spotykane przeze mnie błędy to:błędy hibernacji, które powodują wyświetlanie parametrów w wyrażeniu JPQL zamiast ich przekazywania oraz użycie klauzuli "in" -.

Z powodu pewnych niejasnych błędów hibernacji zdarzają się sytuacje, w których parametry nie są obsługiwane poprawnie i są renderowane do zapytania JPQL (jako przykład sprawdź HHH-6280). Jeśli masz zapytanie, które jest dotknięte takimi defektami i jest wykonywane z dużą szybkością, zniszczy pamięć podręczną planu zapytań, ponieważ każde zapytanie JPQL jest prawie unikalne (zawierające na przykład identyfikatory Twoich jednostek).

Druga kwestia dotyczy sposobu, w jaki hibernacja przetwarza zapytania z klauzulą ​​„in” (np. podaj mi wszystkie encje typu person, których pole id firmy jest jednym z 1, 2, 10, 18). Dla każdej odrębnej liczby parametrów w klauzuli "in", hibernacja wygeneruje inne zapytanie - np. select x from Person x where x.company.id in (:id0_) dla 1 parametru select x from Person x where x.company.id in (:id0_, :id1_) dla 2 parametrów i tak dalej. Wszystkie te zapytania są uważane za różne, jeśli chodzi o pamięć podręczną planu zapytań, co ponownie skutkuje cachethrashingiem. Możesz prawdopodobnie obejść ten problem, pisząc klasę autility tak, aby generowała tylko określoną liczbę parametrów - np. 1,10, 100, 200, 500, 1000. Jeśli np. przekażesz 22 parametry, zwróci listę 100 elementów z 22 parametrami zawartymi w init i pozostałymi 78 parametrami ustawionymi na niemożliwą wartość (np. -1 dla identyfikatorów używane dla kluczy obcych). Zgadzam się, że to brzydki hack, ale da radę wykonać zadanie. W rezultacie będziesz mieć maksymalnie 6 unikalnych zapytań w swojej pamięci podręcznej, a tym samym zmniejszysz thrashing.

Jak więc dowiadujesz się, że masz problem? Możesz napisać dodatkowy kod i wyeksponować metryki z liczbą wpisów w pamięci podręcznej np. przez JMX, dostroić logowanie i analizować logi itp. Jeśli nie chcesz (lub nie możesz) modyfikować aplikacji, możesz po prostu zrzucić stertę i uruchomić na niej to zapytanie OQL (np. za pomocą mat):SELECT l.query.toString() FROM INSTANCEOF org.hibernate.engine.query.spi.QueryPlanCache$HQLQueryPlanKey l . Spowoduje to wyświetlenie wszystkich zapytań aktualnie znajdujących się w dowolnej pamięci podręcznej planu zapytań na stercie. Powinno być dość łatwo zauważyć, czy dotyczy Cię którykolwiek z wyżej wymienionych problemów.

Jeśli chodzi o wpływ na wydajność, trudno powiedzieć, ponieważ zależy to od zbyt wielu czynników. Widziałem bardzo trywialne zapytanie powodujące 10-20 ms narzutu wydanego na tworzenie nowego planu zapytań HQL. Ogólnie rzecz biorąc, jeśli gdzieś jest skrytka, musi być ku temu dobry powód - błąd jest prawdopodobnie drogi, więc powinieneś starać się unikać chybienia tak bardzo, jak to możliwe. Wreszcie, Twoja baza danych będzie musiała również obsłużyć dużą liczbę unikalnych instrukcji SQL — co spowoduje ich przeanalizowanie i być może utworzenie różnych planów wykonania dla każdego z nich.



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Nie udało się znaleźć funkcji konwersji z nieznanej na tekst

  2. BŁĄD:funkcje w wyrażeniu indeksu muszą być oznaczone jako IMMUTABLE w Postgresie

  3. Jak wyświetlić listę wszystkich widoków w bazie danych PostgreSQL

  4. Czy istnieje odpowiednik PostgreSQL profilu SQL Server?

  5. Niekompatybilność Openshift i net-ssh? (2.9.3-beta1 vs 2.9.2)