sql >> Database >  >> RDS >> PostgreSQL

Spring + Hibernate:Query Plan Cache Geheugengebruik

Ik heb dit probleem ook aangekaart. Het komt er in feite op neer dat je een variabel aantal waarden in je IN-clausule en Hibernate hebt om die queryplannen te cachen.

Er zijn twee geweldige blogposts over dit onderwerp. De eerste:

Hibernate 4.2 en MySQL gebruiken in een project met een in-clause-query zoals:select t from Thing t where t.id in (?)

Hibernate slaat deze geparseerde HQL-query's op in de cache. Met name de HibernateSessionFactoryImpl heeft QueryPlanCache met queryPlanCache enparameterMetadataCache . Maar dit bleek een probleem te zijn wanneer het aantal parameters voor de in-clausule groot is en varieert.

Deze caches groeien voor elke afzonderlijke query. Dus deze query met 6000 parameters is niet hetzelfde als 6001.

De in-clause query wordt uitgebreid tot het aantal parameters in decollection. Metadata is opgenomen in het queryplan voor elke parameter in de query, inclusief een gegenereerde naam zoals x10_, x11_ , etc.

Stel je 4000 verschillende variaties voor in het aantal in-clause parametercounts, elk met een gemiddelde van 4000 parameters. De querymetadata voor elke parameter loopt snel op in het geheugen en vult de hoop, aangezien het niet kan worden verzameld met afval.

Dit gaat door totdat alle verschillende variaties in de queryparametertelling in de cache zijn opgeslagen of de JVM geen heapgeheugen meer heeft en begint met throwingjava.lang.OutOfMemoryError:Java-heapruimte.

Het vermijden van in-clauses is een optie, evenals het gebruik van een vaste collectiegrootte voor de parameter (of in ieder geval een kleinere grootte).

Zie de eigenschaphibernate.query.plan_cache_max_size voor het configureren van de maximale grootte van de cache van het queryplan. , standaard ingesteld op 2048 (gemakkelijk te groot voor zoekopdrachten met veel parameters).

En ten tweede (ook waarnaar wordt verwezen vanaf de eerste):

Hibernate gebruikt intern een cache die HQL-statements (asstrings) toewijst aan queryplannen. De cache bestaat uit een begrensde kaart die standaard beperkt is tot 2048 elementen (configureerbaar). Alle HQL-query's worden via deze cache geladen. Bij een misser wordt het item automatisch toegevoegd aan de cache. Dit maakt het erg gevoelig voor thrashing - een scenario waarin we constant nieuwe items in de cache plaatsen zonder ze opnieuw te gebruiken en zo voorkomen dat de cache prestatiewinst oplevert (het voegt zelfs wat cachebeheeroverhead toe). Om het nog erger te maken, is het moeilijk om deze situatie bij toeval te detecteren - je moet de cache expliciet profileren om te merken dat je daar een probleem hebt. Ik zal later een paar woorden zeggen over hoe dit kan worden gedaan.

Dus het geselen van de cache is het gevolg van nieuwe zoekopdrachten die met hoge snelheden worden gegenereerd. Dit kan worden veroorzaakt door een groot aantal problemen. De twee meest voorkomende die ik heb gezien zijn - bugs in de slaapstand die ervoor zorgen dat parameters worden weergegeven in de JPQL-instructie in plaats van als parameters te worden doorgegeven, en het gebruik van een "in" --clausule.

Vanwege enkele obscure bugs in de slaapstand, zijn er situaties waarin parameters niet correct worden afgehandeld en worden weergegeven in de JPQL-query (kijk als voorbeeld naar HHH-6280). Als u een query heeft die wordt beïnvloed door dergelijke defecten en deze wordt met hoge snelheden uitgevoerd, zal uw queryplan-cache verwoesten omdat elke gegenereerde JPQL-query bijna uniek is (met bijvoorbeeld ID's van uw entiteiten).

Het tweede probleem ligt in de manier waarop de slaapstand query's verwerkt met een "in"-clausule (geef me bijvoorbeeld alle persoonsentiteiten waarvan het bedrijfs-idfield een van 1, 2, 10, 18 is). Voor elk onderscheiden aantal parameters in de "in"-clausule, zal hibernate een andere zoekopdracht produceren - bijv. select x from Person x where x.company.id in (:id0_) voor 1 parameter,select x from Person x where x.company.id in (:id0_, :id1_) voor 2 parameters enzovoort. Al deze zoekopdrachten worden als verschillend beschouwd, wat betreft de cache van het queryplan, wat weer resulteert in cachethrashing. U kunt dit probleem waarschijnlijk omzeilen door de autility-klasse te schrijven om slechts een bepaald aantal parameters te produceren - bijv. 1,10, 100, 200, 500, 1000. Als u bijvoorbeeld 22 parameters doorgeeft, retourneert het een lijst van 100 elementen met de 22 parameters erin en de resterende 78 parameters ingesteld op een onmogelijke waarde (bijv. -1voor ID's gebruikt voor buitenlandse sleutels). Ik ben het ermee eens dat dit een lelijke hack is, maar het zou de klus kunnen klaren. Als gevolg hiervan heb je maximaal 6 unieke zoekopdrachten in je cache en dus minder thrashing.

Dus hoe kom je erachter dat je het probleem hebt? Je zou wat extra code kunnen schrijven en metrieken kunnen weergeven met het aantal items in de cache, b.v. over JMX, logboekregistratie afstemmen en de logbestanden analyseren, enz. Als u de toepassing niet wilt (of kunt) wijzigen, kunt u de heap gewoon dumpen en deze OQL-query ertegen uitvoeren (bijvoorbeeld met mat):SELECT l.query.toString() FROM INSTANCEOF org.hibernate.engine.query.spi.QueryPlanCache$HQLQueryPlanKey l . Het zal alle query's uitvoeren die zich momenteel in een queryplancache op uw heap bevinden. Het zou vrij eenvoudig moeten zijn om te zien of je last hebt van een van de bovengenoemde problemen.

Wat de impact op de prestaties betreft, is moeilijk te zeggen omdat het van te veel factoren afhangt. Ik heb een zeer triviale query gezien die 10-20 msof overhead veroorzaakte bij het maken van een nieuw HQL-queryplan. Over het algemeen, als er ergens een cache is, moet daar een goede reden voor zijn - een mis is waarschijnlijk duur, dus je moet proberen om zo veel mogelijk te vermijden. Last but not least, uw database zal ook grote hoeveelheden unieke SQL-instructies moeten verwerken - waardoor het de opdracht kan ontleden en voor elk ervan verschillende uitvoeringsplannen kunnen maken.



  1. Hoe ORDER BY-clausule in SQL te gebruiken?

  2. De beste manier om een ​​audit trail in SQL Server te implementeren?

  3. Profielen maken in Oracle voor gebruikersbeveiliging

  4. Hoe MySQL-querylogboek inschakelen?