Hibernácia medzipamäte druhej úrovne

1. Prehľad

Jednou z výhod vrstiev databázovej abstrakcie, ako sú rámce ORM (objektovo-relačné mapovanie), sú ich schopnosť transparentne ukladať dáta do medzipamäte načítané z podkladového obchodu. To pomáha eliminovať náklady na prístup k databáze pre často prístupné údaje.

Zvýšenie výkonu môže byť významné, ak sú pomery čítania a zápisu do pamäte cache vysoké, najmä pre entity, ktoré pozostávajú z grafov veľkých objektov.

V tomto článku preskúmame medzipamäť Hibernate druhej úrovne.

Vysvetľujeme niektoré základné pojmy a ako vždy všetko ilustrujeme na jednoduchých príkladoch. Používame JPA a prechádzame späť k Hibernate native API iba pre tie funkcie, ktoré nie sú štandardizované v JPA.

2. Čo je to medzipamäť druhej úrovne?

Rovnako ako väčšina ostatných plne vybavených rámcov ORM, aj Hibernate má koncept medzipamäte prvej úrovne. Jedná sa o medzipamäť rozsahu relácie, ktorá zaisťuje, že každá inštancia entity sa v pretrvávajúcom kontexte načíta iba raz.

Po ukončení relácie sa ukončí aj vyrovnávacia pamäť prvej úrovne. To je skutočne žiaduce, pretože to umožňuje súbežným reláciám pracovať s inštanciami entít izolovane od seba.

Na druhej strane vyrovnávacia pamäť druhej úrovne je SessionFactory-scoped, čo znamená, že ho zdieľajú všetky relácie vytvorené v tej istej továrni na relácie. Keď je inštancia entity vyhľadaná podľa jej id (buď logikou aplikácie, alebo interným dlhodobým spánkom, napr. keď načíta asociácie k tejto entite z iných entít) a ak je pre túto entitu povolené ukladanie do medzipamäte druhej úrovne, stane sa nasledovné:

  • Ak je inštancia už v medzipamäti prvej úrovne prítomná, vráti sa odtiaľ
  • Ak sa inštancia nenájde v medzipamäti prvej úrovne a zodpovedajúci stav inštancie sa uloží do medzipamäte druhej úrovne, potom sa odtiaľ načítajú údaje a inštancia sa zhromaždí a vráti
  • V opačnom prípade sa potrebné údaje načítajú z databázy a inštancia sa zhromaždí a vráti

Akonáhle je inštancia uložená v kontexte perzistencie (vyrovnávacia pamäť prvej úrovne), vráti sa odtiaľ vo všetkých nasledujúcich hovoroch v rámci tej istej relácie, kým sa relácia neuzavrie alebo kým sa inštancia z kontextu perzistencie nevystráni manuálne. Stav načítanej inštancie je tiež uložený v medzipamäti L2, ak tam ešte nebol.

3. Regionálna továreň

Hibernácia druhej úrovne ukladania do vyrovnávacej pamäte je navrhnutá tak, aby si neuvedomovala skutočného použitého poskytovateľa medzipamäte. Hibernate je potrebné zabezpečiť iba pri implementácii org.hibernate.cache.spi.RegionFactory rozhranie, ktoré obsahuje všetky podrobnosti špecifické pre skutočných poskytovateľov vyrovnávacej pamäte. V zásade funguje ako most medzi Hibernate a poskytovateľmi medzipamäte.

V tomto článku používame Ehcache ako poskytovateľa medzipamäte, čo je vyspelá a široko používaná vyrovnávacia pamäť. Môžete si samozrejme zvoliť ktoréhokoľvek iného poskytovateľa, pokiaľ existuje implementácia a RegionFactory pre to.

Pridáme implementáciu továrne na oblasť Ehcache do cesty triedy s nasledujúcou závislosťou Maven:

 org.hibernate hibernate-ehcache 5.2.2.Final 

Tu nájdete najnovšiu verziu servera hibernácia-ehcache. Uistite sa však, že hibernácia-ehcache verzia sa rovná verzii hibernácie, ktorú používate vo svojom projekte, napr. ak používate hibernácia-ehcache 5.2.2. Záverečné rovnako ako v tomto príklade by potom mala byť aj verzia Hibernate 5.2.2. Konečné.

The hibernácia-ehcache artefakt má závislosť od samotnej implementácie Ehcache, ktorá je tak prechodne zahrnutá aj do cesty triedy.

4. Povolenie ukladania do pamäte cache druhej úrovne

S nasledujúcimi dvoma vlastnosťami povieme Hibernate, že je povolené ukladanie do vyrovnávacej pamäte L2, a dáme mu názov triedy továrenského regiónu:

hibernate.cache.use_second_level_cache = true hibernate.cache.region.factory_class = org.hibernate.cache.ehcache.EhCacheRegionFactory 

Napríklad v persistence.xml vyzeralo by to ako:

 ...   ... 

Ak chcete zakázať ukladanie do vyrovnávacej pamäte druhej úrovne (napríklad na účely ladenia), stačí nastaviť hibernate.cache.use_second_level_cache majetok na nepravdivý.

5. Urobenie entity do medzipamäte

Za účelom aby bol subjekt oprávnený na ukladanie do medzipamäte druhej úrovne, anotujeme ho konkrétnym spôsobom dlhodobého spánku @ org.hibernate.annotations.Cache anotáciu a určiť stratégiu súbežnosti pamäte cache.

Niektorí vývojári sa domnievajú, že je dobrým zvykom pridať štandard @ javax.persistence.Cacheable anotácia (aj keď to Hibernate nevyžaduje), takže implementácia triedy entity môže vyzerať takto:

@Entity @Cacheable @ org.hibernate.annotations.Cache (usage = CacheConcurrencyStrategy.READ_WRITE) verejná trieda Foo {@Id @GeneratedValue (strategy = GenerationType.AUTO) @Column (name = "ID") súkromné ​​dlhé ID; @Column (name = "NAME") private String name; // zakladatelia a zakladatelia}

Pre každú triedu entít použije Hibernate samostatnú oblasť pamäte cache na ukladanie stavu inštancií pre túto triedu. Názov regiónu je úplný názov triedy.

Napríklad, Foo inštancie sú uložené v pamäti cache s názvom com.baeldung.hibernate.cache.model.Foo v Ehcache.

Na overenie funkčnosti ukladania do pamäte cache môžeme napísať nasledujúci rýchly test:

Foo foo = nový Foo (); fooService.create (foo); fooService.findOne (foo.getId ()); int size = CacheManager.ALL_CACHE_MANAGERS.get (0) .getCache ("com.baeldung.hibernate.cache.model.Foo"). getSize (); assertThat (size, greaterThan (0));

Tu priamo na overenie používame rozhranie Ehcache API com.baeldung.hibernate.cache.model.Foo cache nie je prázdna po načítaní a Foo inštancia.

Môžete tiež povoliť protokolovanie SQL vygenerovaných programom Hibernate a vyvolať ich fooService.findOne (foo.getId ()) niekoľkokrát v teste, aby sa overilo, či vyberte výpis pre načítanie Foo sa vytlačí iba raz (prvýkrát), čo znamená, že pri nasledujúcich hovoroch sa inštancia entity získa z medzipamäte.

6. Stratégia súbežnosti vyrovnávacej pamäte

Na základe prípadov použitia si môžeme zvoliť jednu z nasledujúcich stratégií súbežnosti vyrovnávacej pamäte:

  • IBA NA ČÍTANIE: Používa sa iba pre entity, ktoré sa nikdy nezmenia (výnimka sa vyvolá, ak dôjde k pokusu o aktualizáciu takejto entity). Je to veľmi jednoduché a výkonné. Veľmi vhodné pre niektoré statické referenčné údaje, ktoré sa nemenia
  • NONSTRICT_READ_WRITE: Cache sa aktualizuje po potvrdení transakcie, ktorá zmenila dotknuté údaje. Silná konzistencia teda nie je zaručená a existuje malé časové okno, v ktorom je možné získať zastarané údaje z medzipamäte. Tento druh stratégie je vhodný pre prípady použitia, ktoré môžu tolerovať prípadnú konzistenciu
  • ČÍTAJ PÍŠ: Táto stratégia zaručuje silnú konzistenciu, ktorú dosahuje použitím „mäkkých“ zámkov: Keď sa aktualizuje entita vo vyrovnávacej pamäti, soft lock sa uloží do medzipamäte aj pre túto entitu, ktorá sa uvoľní po vykonaní transakcie. Všetky súbežné transakcie, ktoré pristupujú k blokovaným položkám, načítajú zodpovedajúce údaje priamo z databázy
  • TRANSAKČNÉ: Zmeny medzipamäte sa vykonávajú v distribuovaných transakciách XA. Zmena v entite uloženej v pamäti je potvrdená alebo vrátená späť v databáze aj pamäti cache v tej istej transakcii XA

7. Správa cache

Ak nie sú definované zásady expirácie a vysťahovania, vyrovnávacia pamäť by mohla rásť donekonečna a nakoniec by spotrebovala všetku dostupnú pamäť. Vo väčšine prípadov Hibernate prenecháva tieto úlohy správy vyrovnávacej pamäte poskytovateľom vyrovnávacej pamäte, pretože sú skutočne špecifické pre každú implementáciu vyrovnávacej pamäte.

Napríklad by sme mohli definovať nasledujúcu konfiguráciu Ehcache, aby sme obmedzili maximálny počet cache Foo inštancie do 1 000:

8. Zberná medzipamäť

Zbierky sa predvolene neukladajú do medzipamäte a musíme ich výslovne označiť ako medzipamäť. Napríklad:

@Entity @Cacheable @ org.hibernate.annotations.Cache (usage = CacheConcurrencyStrategy.READ_WRITE) verejná trieda Foo {... @Cacheable @ org.hibernate.annotations.Cache (usage = CacheConcurrencyStrategy.READ_WRITE) @OneToMany súkromné ​​zbierkové pruhy; // zakladatelia a zakladatelia}

9. Interné zastúpenie štátu vo vyrovnávacej pamäti

Entity sa neukladajú do medzipamäte druhej úrovne ako inštancie jazyka Java, ale skôr v rozloženom (hydratovanom) stave:

  • Id (primárny kľúč) nie je uložený (je uložený ako súčasť kľúča medzipamäte)
  • Prechodné vlastnosti sa neukladajú
  • Zbierky nie sú uložené (ďalšie podrobnosti nájdete nižšie)
  • Hodnoty vlastností, ktoré nie sú združené, sú uložené v pôvodnej podobe
  • Pre ID je uložený iba id (cudzí kľúč) Do jedného združenia

Toto zobrazuje všeobecný dizajn medzipamäte druhej úrovne režimu spánku, v ktorom model medzipamäte odráža základný relačný model, ktorý je priestorovo efektívny a uľahčuje ich synchronizáciu.

9.1. Interné zastúpenie zbierok v archíve

Už sme spomenuli, že musíme výslovne uviesť, že zbierka (OneToMany alebo ManyToMany asociácia) je cachovateľná, inak sa neuchováva.

Režim dlhodobého spánku v skutočnosti ukladá zbierky do samostatných oblastí medzipamäte, jeden pre každú zbierku. Názov regiónu je úplný názov triedy plus názov vlastnosti kolekcie, napríklad: com.baeldung.hibernate.cache.model.Foo.bars. To nám dáva flexibilitu pri definovaní samostatných parametrov pamäte cache pre zbierky, napr. politika vysťahovania / vypršania platnosti.

Je tiež dôležité spomenúť, že pre každý záznam kolekcie sa ukladajú do medzipamäte iba ID entít obsiahnutých v kolekcii, čo znamená, že vo väčšine prípadov je dobré tiež urobiť cacheované entity.

10. Invalidácia vyrovnávacej pamäte pre dotazy a natívne dotazy v štýle HQL DML

Pokiaľ ide o HQL v štýle DML (vložiť, aktualizovať a vymazať HQL) dokáže Hibernate určiť, ktorých entít sa tieto operácie týkajú:

entityManager.createQuery ("aktualizovať sadu Foo ... kde ..."). executeUpdate ();

V tomto prípade sú všetky inštancie Foo vylúčené z medzipamäte L2, zatiaľ čo ostatný obsah v medzipamäti zostáva nezmenený.

Pokiaľ však ide o natívne príkazy SQL DML, program Hibernate nedokáže odhadnúť, čo sa aktualizuje, takže zneplatňuje celú medzipamäť druhej úrovne:

session.createNativeQuery ("aktualizovať FOO set ... kde ..."). executeUpdate ();

Toto asi nie je to, čo chcete! Riešením je povedať spoločnosti Hibernate, ktoré entity sú ovplyvnené natívnymi príkazmi DML, aby mohla vysťahovať iba položky súvisiace s Foo subjekty:

Dotaz nativeQuery = entityManager.createNativeQuery ("aktualizovať sadu FOO ... kde ..."); nativeQuery.unwrap (org.hibernate.SQLQuery.class) .addSynchronizedEntityClass (Foo.class); nativeQuery.executeUpdate ();

Príliš sme sa vrátili k pôvodnému spánku SQLQuery API, pretože táto funkcia nie je (zatiaľ) definovaná v JPA.

Vyššie uvedené sa vzťahuje iba na vyhlásenia DML (vložiť, aktualizovať, vymazať a natívne volania funkcií / procedúr). Nativní vyberte dotazy neznehodnocujú vyrovnávaciu pamäť.

11. Cache dotazov

Výsledky dotazov HQL je tiež možné uložiť do medzipamäte. Je to užitočné, ak často vykonávate dopyt na entitách, ktoré sa zriedka menia.

Ak chcete povoliť medzipamäť dotazov, nastavte hodnotu hibernate.cache.use_query_cache majetok do pravda:

hibernate.cache.use_query_cache = pravda

Potom musíte pre každý dopyt výslovne uviesť, že je dopyt uložiteľný do medzipamäte (pomocou org.hibernate.cacheable nápoveda k dotazu):

entityManager.createQuery ("vyberte f z Foo f") .setHint ("org.hibernate.cacheable", true) .getResultList ();

11.1. Osvedčené postupy pre dotaz na medzipamäť

Tu sú niektoré pokyny a najlepšie postupy týkajúce sa ukladania dotazov do pamäte cache:

  • Ako je to v prípade kolekcií, do vyrovnávacej pamäte sa ukladajú iba identifikácie entít vrátených v dôsledku dotazu uloženého do medzipamäte, takže sa dôrazne odporúča, aby bola pre tieto entity povolená vyrovnávacia pamäť druhej úrovne.
  • Pre každú kombináciu hodnôt parametrov dotazu (väzobné premenné) existuje jeden záznam do medzipamäte, takže dotazy, pri ktorých očakávate veľa rôznych kombinácií hodnôt parametrov, nie sú vhodnými kandidátmi na ukladanie do pamäte cache.
  • Dotazy, ktoré zahŕňajú triedy entít, pre ktoré v databáze často dochádza k zmenám, tiež nie sú vhodnými kandidátmi na ukladanie do pamäte cache, pretože budú zneplatnené vždy, keď dôjde k zmene súvisiacej s niektorou z entít zaradených do dotazu bez ohľadu na to, či sú zmenené inštancie do pamäte cache ako súčasť výsledku dotazu alebo nie.
  • V predvolenom nastavení sú všetky výsledky medzipamäte dotazu uložené v priečinku org.hibernate.cache.internal.StandardQueryCache regiónu. Rovnako ako v prípade ukladania do pamäte cache pre entity / kolekcie, môžete prispôsobiť parametre medzipamäte pre túto oblasť a definovať politiky vysťahovania a vypršania platnosti podľa svojich potrieb. Pre každý dotaz môžete tiež určiť názov vlastnej oblasti, aby ste mohli poskytnúť rôzne nastavenia pre rôzne dotazy.
  • Pre všetky tabuľky, ktoré sú dotazované ako súčasť dotazov uložených do medzipamäte, režim dlhodobého spánku uchováva časové značky poslednej aktualizácie v samostatnej oblasti s názvom org.hibernate.cache.spi.UpdateTimestampsCache. Uvedomenie si tejto oblasti je veľmi dôležité, ak používate ukladanie do pamäte cache dotazov, pretože režim dlhodobého spánku ho používa na overenie, či výsledky dotazu v pamäti nie sú zastarané. Položky v tejto vyrovnávacej pamäti nesmú byť vypršané alebo vypršané, pokiaľ existujú výsledky dotazov v pamäti uložené pre príslušné tabuľky v regiónoch s výsledkami dotazov. Najlepšie je vypnúť automatické vysťahovanie a vypršanie platnosti pre túto oblasť medzipamäte, pretože aj tak nespotrebováva veľa pamäte.

12. Záver

V tomto článku sme sa pozreli na to, ako nastaviť medzipamäť Hibernate druhej úrovne. Videli sme, že je pomerne ľahké ho nakonfigurovať a používať, pretože Hibernate robí všetko pre to, aby spoznal zákulisie, vďaka čomu je využitie medzipamäte druhej úrovne transparentné pre obchodnú logiku aplikácií.

Implementácia tohto Výukového programu vyrovnávacej pamäte medzipamäte druhej úrovne je k dispozícii na serveri Github. Toto je projekt založený na Maven, takže by malo byť ľahké ho importovať a spustiť tak, ako je.


$config[zx-auto] not found$config[zx-overlay] not found