Java >> Java opplæring >  >> Tag >> Spring

Ytelsesjustering av vårstøvel

Innledning

Selv om det er ganske enkelt å utvikle en Spring Boot-applikasjon, er det en mer utfordrende oppgave å justere ytelsen til en Spring Boot-applikasjon, siden det ikke bare krever at du forstår hvordan Spring-rammeverket fungerer bak kulissene, men du må vite hva som er beste måten å bruke det underliggende rammeverket for datatilgang, som for eksempel Hibernate.

I en tidligere artikkel viste jeg deg hvor enkelt du kan optimalisere ytelsen til Petclinic-demoapplikasjonen. Som standard bruker imidlertid Petclinic Spring Boot-applikasjonen HSQLDB-databasen i minnet, som ikke er det du vanligvis vil at applikasjonen skal optimaliseres for.

I denne artikkelen skal vi bytte den underliggende databasen fra HSQLDB i minnet til MySQL og kjøre Hypersistence Optimizer for å generere en ytelsesjusteringsrapport for JPA- og Hibernate-datatilgangslaget i sammenheng med MySQL-databasen.

Å justere ytelsen til en #SpringBoot-app kan være utfordrende. @vlad_mihalcea viser deg #howto generere en ytelsesjusteringsrapport for #JPA- og Hibernate-datatilgangslaget i konteksten av #MySQL-databasen. https://t.co/V2CKQNcg4x pic.twitter.com/5Ii40fGXHo

— Java (@java) 6. november 2019

Vår Petclinic

Spring Petclinic er en demoapplikasjon bygget ved hjelp av Spring Boot, som demonstrerer rammeverkets evner.

Som standard bruker Spring Boot HSQLDB, men mens denne minnedatabasen brukes mye for testing, i et produksjonsmiljø, er det mer sannsynlig at du bruker en database som MySQL eller PostgreSQL.

Heldigvis tilbyr Spring Boot en MySQL-konfigurasjon og en Spring-profil, som vi kan bruke som utgangspunkt for analysen vår.

Bytter tester til å bruke MySQL-profilen

Først av alt må vi bruke @ActiveProfiles merknad for å aktivere mysql Vårprofil.

@RunWith(SpringRunner.class)
@SpringBootTest
@ActiveProfiles("mysql")
public class PetclinicIntegrationTests {

    @Autowired
    private VetRepository vets;

    @Test
    public void testFindAll() throws Exception {
        vets.findAll();
        vets.findAll(); // served from cache
    }
}

Etter å ha aktivert mysql Spring-profil, Spring Boot kommer til å bruke application-mysql.properties konfigurasjonsfil for å overstyre innstillinger fra standard application.properties innstillingsfil.

I vårt tilfelle var den eneste endringen som var nødvendig i application-mysql.properties konfigurasjonsfilen skulle endre påloggingsinformasjonen for databasetilkoblingen:

database=mysql

spring.datasource.url=jdbc:mysql://localhost/petclinic
spring.datasource.username=mysql
spring.datasource.password=admin

Kjøre Hypersistence Optimizer

Som du kan se i PetclinicIntegrationTests klasse, er det veldig enkelt å kjøre Hypersitece Optimizer, siden du bare trenger å bestå EntityManagerFactory forekomst til HypersistenceOptimizer objektkonstruktør, og kall init metode.

Hypersistence Optimizer : CRITICAL - TableGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.owner.Pet] entity uses the TABLE strategy, which does not scale very well. Consider using the IDENTITY identifier strategy instead, even if it does not allow JDBC batch inserts.
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#TableGeneratorEvent

Hypersistence Optimizer : CRITICAL - TableGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.owner.Owner] entity uses the TABLE strategy, which does not scale very well. Consider using the IDENTITY identifier strategy instead, even if it does not allow JDBC batch inserts.
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#TableGeneratorEvent

Hypersistence Optimizer : CRITICAL - TableGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.visit.Visit] entity uses the TABLE strategy, which does not scale very well. Consider using the IDENTITY identifier strategy instead, even if it does not allow JDBC batch inserts.
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#TableGeneratorEvent

Hypersistence Optimizer : CRITICAL - TableGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.owner.PetType] entity uses the TABLE strategy, which does not scale very well. Consider using the IDENTITY identifier strategy instead, even if it does not allow JDBC batch inserts.
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#TableGeneratorEvent

Hypersistence Optimizer : CRITICAL - TableGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.vet.Specialty] entity uses the TABLE strategy, which does not scale very well. Consider using the IDENTITY identifier strategy instead, even if it does not allow JDBC batch inserts.
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#TableGeneratorEvent

Hypersistence Optimizer : CRITICAL - TableGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.vet.Vet] entity uses the TABLE strategy, which does not scale very well. Consider using the IDENTITY identifier strategy instead, even if it does not allow JDBC batch inserts.
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#TableGeneratorEvent

Hypersistence Optimizer : MAJOR - DialectVersionEvent - 
Your application is using the [org.hibernate.dialect.MySQL5Dialect] Hibernate-specific Dialect. Consider using the [org.hibernate.dialect.MySQL57Dialect] instead as it's closer to your current database server version [MySQL 8.0].
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#DialectVersionEvent

TableGeneratorEvent

TableGeneratorEvent utløses fordi vi i forrige artikkel byttet enhetsidentifikatorstrategien fra IDENTITY til SEQUENCE .

Bruke SEQUENCE identifikatorstrategi er det beste alternativet hvis og bare hvis den underliggende databasen støtter sekvensobjekter. MySQL 8 støtter imidlertid ikke databasesekvenser, og av denne grunn bytter Hibernate til å bruke TABLE generator , og, som forklart i artikkelen hans, TABLE generator er et forferdelig valg når det gjelder applikasjonsytelse.

Bytter til å bruke IDENTITY for MySQL

Så i vårt tilfelle er vi bedre å bruke IDENTITY generator for MySQL. Vi ønsker imidlertid ikke å endre enhetstilordningene til IDENTITY som at vi kanskje vil distribuere Petclinic-applikasjonen på andre databasesystemer, som Oracle, SQL Server eller PostgreSQL, som støtter databasesekvenser. Så vi vil at standardtilordningen skal bruke SEQUENCE strategi, men bare endre det til IDENTITY for MySQL.

Selv om IDENTITY generator forhindrer Hibernate fra å samle INSERT-setninger på JDBC-nivå, det er fortsatt bedre å bruke denne strategien i stedet for TABLE generator. For mer informasjon om JDBC batchoppdateringer, sjekk ut denne artikkelen.

Og løsningen er faktisk veldig enkel. Faktisk har jeg allerede forklart det i denne artikkelen.

Ved å gi en MySQL-spesifikk orm.xml JPA-konfigurasjonsfilen som overstyrer baseklassens enhetsidentifikatorstrategi, kan vi bytte til ved å bruke IDENTITY når du bruker MySQL.

Så vi skal lage en orm.xml fil som skal distribueres av mysql profil i META-INF mappen i applikasjonen jar fil.

orm.xml konfigurasjonsfilen ser ut som følger:

<?xml version="1.0" encoding="UTF-8"?>

<entity-mappings 
    xmlns="http://xmlns.jcp.org/xml/ns/persistence/orm"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/persistence/orm_2_2.xsd"
    version="2.2">

    <mapped-superclass 
        class="org.springframework.samples.petclinic.model.BaseEntity">
        <attributes>
            <id name="id">
                <generated-value strategy="IDENTITY"/>
            </id>
        </attributes>
    </mapped-superclass>

</entity-mappings>

Det er det!

Nå, når du kjører PetclinicIntegrationTests på nytt testcase, vil Hypersistence Optimizer generere følgende rapport:

Hypersistence Optimizer : MINOR - IdentityGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.owner.Pet] entity uses the [IdentityGenerator] strategy, 
which prevents Hibernate from enabling JDBC batch inserts. 
Since the database does not support the SEQUENCE identifier strategy, 
you need to use plain JDBC or some other data access framework to batch insert statements. 
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#IdentityGeneratorEvent

Hypersistence Optimizer : MINOR - IdentityGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.owner.Owner] entity uses the [IdentityGenerator] strategy, 
which prevents Hibernate from enabling JDBC batch inserts. 
Since the database does not support the SEQUENCE identifier strategy, you need to use plain JDBC or some other data access framework to batch insert statements. 
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#IdentityGeneratorEvent

Hypersistence Optimizer : MINOR - IdentityGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.visit.Visit] entity uses the [IdentityGenerator] strategy, 
which prevents Hibernate from enabling JDBC batch inserts. 
Since the database does not support the SEQUENCE identifier strategy, you need to use plain JDBC or some other data access framework to batch insert statements. 
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#IdentityGeneratorEvent

Hypersistence Optimizer : MINOR - IdentityGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.owner.PetType] entity uses the [IdentityGenerator] strategy, 
which prevents Hibernate from enabling JDBC batch inserts. 
Since the database does not support the SEQUENCE identifier strategy, you need to use plain JDBC or some other data access framework to batch insert statements. 
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#IdentityGeneratorEvent

Hypersistence Optimizer : MINOR - IdentityGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.vet.Specialty] entity uses the [IdentityGenerator] strategy, 
which prevents Hibernate from enabling JDBC batch inserts. 
Since the database does not support the SEQUENCE identifier strategy, you need to use plain JDBC or some other data access framework to batch insert statements. 
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#IdentityGeneratorEvent

Hypersistence Optimizer : MINOR - IdentityGeneratorEvent - 
The [id] identifier attribute in the [org.springframework.samples.petclinic.vet.Vet] entity uses the [IdentityGenerator] strategy, 
which prevents Hibernate from enabling JDBC batch inserts. 
Since the database does not support the SEQUENCE identifier strategy, you need to use plain JDBC or some other data access framework to batch insert statements. 
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#IdentityGeneratorEvent

Hypersistence Optimizer : MAJOR - DialectVersionEvent - 
Your application is using the [org.hibernate.dialect.MySQL5Dialect] Hibernate-specific Dialect. Consider using the [org.hibernate.dialect.MySQL57Dialect] instead as it's closer to your current database server version [MySQL 8.0].
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#DialectVersionEvent

Legg merke til at TableGeneratorEvent ble erstattet av IdentityGenerator . IdentityGenerator er generert fordi IDENTITY strategi forhindrer Hibernate fra å samle INSERT-setninger når du ringer persist ettersom den trenger å kjenne enhetsidentifikatoren når den knytter den vedvarende enheten til cachen på første nivå.

Men siden vi egentlig ikke har et alternativ for IdentityGenerator når vi bruker MySQL, kan vi ganske enkelt velge å ignorere denne hendelsen i vårt tilfelle.

Filtrering av Hyperstistence Optimizer-hendelser

Hypersitece Optimizer er veldig fleksibel. Du kan tilpasse hvordan hendelsene håndteres, om du vil at de skal logges eller samles til en List , og du kan velge å filtrere hendelser også.

For å filtrere ut IdentityGeneratorEvent , må du konfigurere HyperstistenceOptimizer bønne som dette:

@Configuration
public class HypersistenceConfiguration {
    @Bean
    public HypersistenceOptimizer hypersistenceOptimizer(
            EntityManagerFactory entityManagerFactory) {
        return new HypersistenceOptimizer(
            new JpaConfig(entityManagerFactory)
                .setEventFilter(
                    event -> !(event instanceof IdentityGeneratorEvent)
                )
        );
    }
}

Nå, når du kjører PetclinicIntegrationTests på nytt testtilfelle, vil Hypersistence Optimizer forhindre IdentityGeneratorEvent fra å bli inkludert i rapporten:

Hypersistence Optimizer : MAJOR - DialectVersionEvent - 
Your application is using the [org.hibernate.dialect.MySQL5Dialect] Hibernate-specific Dialect. Consider using the [org.hibernate.dialect.MySQL57Dialect] instead as it's closer to your current database server version [MySQL 8.0].
For more info about this event, check out this User Guide link - https://vladmihalcea.com/hypersistence-optimizer/docs/user-guide/#DialectVersionEvent

DialectVersionEvent

DialectVersionEvent genereres fordi den standardkonfigurerte dvaledialekten er MySQL57Dialect mens Petclinic-applikasjonen kjører mot MySQL 8.

Så vi må bare inkludere MySQL8Dialect i Spring Boot application-mysql.properties konfigurasjonsfil:

database=mysql

spring.datasource.url=jdbc:mysql://localhost/petclinic
spring.datasource.username=mysql
spring.datasource.password=admin

spring.jpa.properties.hibernate.dialect = org.hibernate.dialect.MySQL8Dialect

Og nå rapporteres ingen problemer av Hypersistence Optimizer. Kult, ikke sant?

Konklusjon

Hypersistence Optimizer er et veldig kraftig verktøy som kan hjelpe deg med å oppdage ytelsesproblemer med JPA og Hibernate lenge før de treffer produksjonssystemet.

Og en av dens største fordeler er at den kan utføre alle disse kontrollene på hver forpliktelse, slik at du ikke lenger vil overse en JPA eller Hibernate ytelsesrelatert forbedring på grunn av en veldig stram utviklingsplan.

Alle optimaliseringene som presenteres i denne artikkelen kan bli funnet i dette GitHub-repositoriet.


Java Tag