sql >> Database teknologi >  >> RDS >> PostgreSQL

Spring + Hibernate:Forespørgselsplan Cache Hukommelsesbrug

Jeg har også ramt dette problem. Det går dybest set ned til at have et variabelt antal værdier i din IN-klausul og Hibernate, der prøver at cache disse forespørgselsplaner.

Der er to gode blogindlæg om dette emne. Det første:

Brug af Hibernate 4.2 og MySQL i et projekt med en in-clause-forespørgsel som:select t from Thing t where t.id in (?)

Hibernate cacher disse parsede HQL-forespørgsler. Specifikt HibernateSessionFactoryImpl har QueryPlanCache med queryPlanCache og parameterMetadataCache . Men dette viste sig at være et problem, når antallet af parametre for in-clausen er stort og varierer.

Disse caches vokser for hver enkelt forespørgsel. Så denne forespørgsel med 6000 parametre er ikke det samme som 6001.

In-clause-forespørgslen udvides til antallet af parametre i samlingen. Metadata er inkluderet i forespørgselsplanen for hver parameter i forespørgslen, inklusive et genereret navn som x10_, x11_ osv.

Forestil dig 4000 forskellige variationer i antallet af in-clause parametertællinger, hver af disse med et gennemsnit på 4000 parametre. Forespørgselsmetadataene for hver parameter lægges hurtigt op i hukommelsen og fylder dyngen op, da den ikke kan opsamles skrald.

Dette fortsætter, indtil alle forskellige variationer i forespørgselsparametertællingen er cachelagret, eller JVM løber tør for heap-hukommelse og begynder at throwingjava.lang.OutOfMemoryError:Java-heap-plads.

At undgå in-klausuler er en mulighed, såvel som at bruge en fast samlingsstørrelse for parameteren (eller i det mindste en mindre størrelse).

Se egenskabenhibernate.query.plan_cache_max_size for at konfigurere forespørgselsplanens cache-maks. , der som standard er 2048 (let værktøj til forespørgsler med mange parametre).

Og for det andet (også refereret fra den første):

Hibernate bruger internt en cache, der kortlægger HQL-sætninger (asstrings) til at forespørge planer. Cachen består af et afgrænset kort, der som standard er begrænset til 2048 elementer (kan konfigureres). Alle HQL-forespørgsler indlæses gennem denne cache. I tilfælde af en glip føjes posten automatisk til cachen. Dette gør det meget modtageligt for thrashing - ascenario, hvor vi konstant lægger nye poster ind i cachen uden nogensinde at genbruge dem og dermed forhindrer cachen i at give nogen præstationsgevinster (det tilføjer endda nogle cache-administration overhead). For at gøre tingene værre, er det svært at opdage denne situation tilfældigt - du skal udtrykkeligt profilere cachen for at bemærke, at du har et problem der. Jeg vil sige et par ord om, hvordan dette kunne gøres senere.

Så cache-tæsk er resultatet af nye forespørgsler, der genereres med høje hastigheder. Dette kan skyldes en lang række problemer. De to mest almindelige, som jeg har set, er - fejl i dvaletilstand, som forårsager, at parametre gengives i JPQL-sætningen i stedet for at blive sendt asparametre og brugen af ​​en "in" - klausul.

På grund af nogle obskure fejl i dvaletilstand, er der situationer, hvor parametre ikke håndteres korrekt og gengives i JPQLquery (tjek som et eksempel HHH-6280). Hvis du har en forespørgsel, der er påvirket af sådanne defekter, og den udføres med høje hastigheder, vil den ødelægge din forespørgselsplans cache, fordi hver JPQL-forespørgsel, der genereres, er næsten unik (indeholder f.eks. ID'er for dine enheder).

Det andet problem ligger i den måde, at dvale behandler forespørgsler med en "in"-klausul (giv mig f.eks. alle personenheder, hvis virksomheds-id-felt er et af 1, 2, 10, 18). For hvert enkelt antal parametre i "in"-klausulen, vil hibernate producere en anden forespørgsel - f.eks.select x from Person x where x.company.id in (:id0_) for 1 parameter,select x from Person x where x.company.id in (:id0_, :id1_) for 2 parametre og så videre. Alle disse forespørgsler betragtes som forskellige, hvad angår forespørgselsplanens cache, hvilket igen resulterer i cachethrashing. Du kunne sikkert løse dette problem ved at skrive en hjælpeklasse til kun at producere et bestemt antal parametre - f.eks. 1,10, 100, 200, 500, 1000. Hvis du f.eks. sender 22 parametre, vil det returnere en liste med 100 elementer med de 22 parametre inkluderet i det og de resterende 78 parametre sat til en umulig værdi (f.eks. -1 for ID'er bruges til fremmednøgler). Jeg er enig i, at dette er et grimt hack, men kunne få arbejdet gjort. Som et resultat vil du højst have 6 unikke forespørgsler i din cache og dermed reducere thrashing.

Så hvordan finder du ud af, at du har problemet? Du kan skrive en ekstra kode og afsløre metrics med antallet af poster i cachen, f.eks. over JMX, tune logning og analysere logfilerne osv. Hvis du ikke vil (eller ikke kan) ændre applikationen, kan du bare dumpe heapen og køre denne OQL-forespørgsel mod den (f.eks. ved at bruge mat):SELECT l.query.toString() FROM INSTANCEOF org.hibernate.engine.query.spi.QueryPlanCache$HQLQueryPlanKey l . Det vil udlæse alle forespørgsler, der i øjeblikket er placeret i enhver forespørgselsplan-cache på din heap. Det burde være ret nemt at få øje på, om du er berørt af nogen af ​​de førnævnte problemer.

Hvad angår præstationspåvirkningen, er det svært at sige, da det afhænger af for mange faktorer. Jeg har set en meget triviel forespørgsel, der forårsager 10-20 msof overhead brugt på at skabe en ny HQL-forespørgselsplan. Generelt, hvis der er en cache et eller andet sted, skal der være en god grund til det - amiss er sandsynligvis dyrt, så du bør prøve at undgå glip så meget som muligt. Sidst, men ikke mindst, skal din database også håndtere store mængder af unikke SQL-sætninger - hvilket får den til at analysere dem og måske oprette forskellige eksekveringsplaner for hver enkelt af dem.



  1. INDSTIL TEKSTSTØRRELSE Virker ikke i SQL Server? Tjek det her.

  2. Er der en RIGTIG ydelsesforskel mellem INT og VARCHAR primære nøgler?

  3. SQL CREATE TABLE Syntaks – Listet efter DBMS

  4. Søgeord ikke understøttet:Metadata