Java >> Programma Java >  >> Tag >> hibernate

Ottimizzazione delle prestazioni di ibernazione eseguita correttamente

L'ottimizzazione delle prestazioni dell'applicazione è un'attività complessa e specifica dell'applicazione. Tutti i modelli di dominio sono diversi e, abbastanza spesso, anche la quantità di dati gestiti da essi differisce significativamente tra più installazioni. In aggiunta a ciò, quasi tutte le tecniche di ottimizzazione delle prestazioni hanno dei compromessi, che non le rendono adatte a tutte le situazioni.

Per questo motivo, seguire le migliori pratiche e i consigli generali non è sufficiente per implementare un livello di persistenza efficiente e ad alte prestazioni. Molto probabilmente eviterai le insidie ​​più evidenti delle prestazioni, ma perderai anche tutti i problemi specifici dell'applicazione. Allo stesso tempo, aumenterai la complessità del tuo livello di persistenza e impiegherai tempo a implementare ottimizzazioni delle prestazioni che non sono rilevanti per la tua applicazione e i tuoi dati.

Se vuoi farlo bene, devi adottare un approccio diverso. Uno che ti consente di utilizzare il tuo tempo in modo efficiente e ti assicura di risolvere i problemi di prestazioni rilevanti. Puoi farlo solo se hai la mentalità giusta e le informazioni necessarie per scegliere la migliore funzione di ottimizzazione delle prestazioni per ogni situazione.

Minalità delle prestazioni

Parliamo prima della mentalità. Imposta il tema per le sezioni seguenti definendo cosa si desidera ottimizzare e quando farlo.

Uno degli errori più comuni è che gli sviluppatori cercano di prevenire tutti i possibili problemi teorici di prestazioni prima che si verifichino nei test o nella produzione. Ciò aggiunge molta complessità, rende più difficile la manutenzione del codice e rallenta lo sviluppo fornendo solo un valore minimo agli utenti. Questo è comunemente noto come ottimizzazione prematura.

Il vero problema è che i programmatori hanno passato troppo tempo a preoccuparsi dell'efficienza nei posti sbagliati e nei momenti sbagliati; l'ottimizzazione prematura è la radice di tutti i mali (o almeno della maggior parte di essi) nella programmazione.

Donald Knuth – L'arte della programmazione informatica

Per evitarlo, devi decidere con saggezza quali parti del tuo codice vuoi ottimizzare e quando farlo.

Cosa dovresti ottimizzare?

C'è una risposta ovvia a questa domanda:tutte le parti che sono troppo lente!

Ma come trovi queste parti?

Non li troverai leggendo le migliori pratiche o seguendo i consigli degli analizzatori di codice statico. Questo non è perché queste cose sono generalmente sbagliate. È perché entrambi gli approcci mancano di 2 informazioni importanti:

  1. La quantità di dati con cui stai lavorando.
  2. Il numero di richieste parallele che il tuo sistema deve gestire.

Entrambi hanno un forte impatto sulle prestazioni della tua applicazione o, dovrei dire meglio, sulle inefficienze che puoi accettare nel tuo codice. Ad esempio:

  • Puoi gestire più associazioni che non contengono mai più di 3 elementi in modo molto inefficiente senza riscontrare problemi di prestazioni. Ma non puoi farlo con un'associazione che fa riferimento a mille record.
  • Se stai creando un'applicazione interna che viene utilizzata solo da 20 utenti contemporaneamente, puoi facilmente utilizzare funzionalità come l'annotazione @Formula di Hibernate per migliorare la velocità di sviluppo. Ma se lo fai in un'applicazione webscale, la complessità dell'istruzione SQL generata molto probabilmente causerà problemi di prestazioni.

Questi esempi mostrano che devi analizzare come si comporta il tuo livello di persistenza in uno scenario di produzione.

Quando dovresti ottimizzare?

La citazione di Donald e la sezione precedente hanno già risposto a questa domanda. Per evitare di lavorare sui miglioramenti delle prestazioni sbagliati, è necessario identificare quelli rilevanti. Ciò significa che devi dimostrare che il problema di prestazioni esiste già in produzione o che esisterà presto in produzione.

Dopo averlo fatto, sai che lo sforzo che impiegherai e la complessità che aggiungerai al tuo sistema forniranno valore ai tuoi utenti.

Analisi delle prestazioni

Prima di iniziare a migliorare le prestazioni del tuo livello di persistenza, devi identificare le parti che devono essere migliorate. Ci sono diversi modi per farlo. In questo articolo, voglio mostrare 2 opzioni che si concentrano sulle operazioni interne di Hibernate e non richiedono un profiler.

Statistiche di ibernazione

Il modo più semplice per monitorare le operazioni interne di Hibernate e le query del database è attivare il componente statistico di Hibernate. Puoi farlo impostando la proprietà di sistema hibernate.generate_statistics a vero . Oppure puoi impostare il parametro nel tuo persistence.xml configurazione.

<persistence>
    <persistence-unit name="my-persistence-unit">
		...
        <properties>
			<property name="hibernate.generate_statistics" value="true" />
			...
        </properties>
    </persistence-unit>
</persistence>

Dopo averlo fatto, Hibernate scriverà le seguenti istruzioni di registro nel file di registro.

2021-02-22 20:28:52,484 DEBUG [org.hibernate.stat.internal.ConcurrentStatisticsImpl] (default task-1) HHH000117: HQL: Select p From Product p, time: 0ms, rows: 10
2021-02-22 20:28:52,484 INFO  [org.hibernate.engine.internal.StatisticalLoggingSessionEventListener] (default task-1) Session Metrics {
    8728028 nanoseconds spent acquiring 12 JDBC connections;
    295527 nanoseconds spent releasing 12 JDBC connections;
    12014439 nanoseconds spent preparing 21 JDBC statements;
    5622686 nanoseconds spent executing 21 JDBC statements;
    0 nanoseconds spent executing 0 JDBC batches;
    0 nanoseconds spent performing 0 L2C puts;
    0 nanoseconds spent performing 0 L2C hits;
    0 nanoseconds spent performing 0 L2C misses;
    403863 nanoseconds spent executing 1 flushes (flushing a total of 10 entities and 0 collections);
    25529864 nanoseconds spent executing 1 partial-flushes (flushing a total of 10 entities and 10 collections)
}

Per ogni query eseguita, Hibernate scriverà un messaggio contenente l'istruzione fornita, il tempo impiegato per eseguirla e il numero di righe restituite. Ciò rende facile individuare query lente o molto complesse o quelle che restituiscono migliaia di righe.

Alla fine della sessione, Hibernate riepiloga anche tutte le query eseguite, i batch JDBC utilizzati, le interazioni della cache di 2° livello e gli svuotamenti eseguiti. Questo riepilogo è sempre un ottimo punto di partenza per l'analisi delle prestazioni. Ti mostra se Hibernate ha causato il problema di prestazioni e che tipo di problema si tratta. Ecco alcuni esempi:

Se Hibernate ha eseguito molte più istruzioni del previsto, probabilmente hai un problema di selezione n+1. Spiego come analizzarlo e risolverlo in questo video corso gratuito in 3 parti.

Se il numero di query è basso ma il tempo di esecuzione è elevato, puoi trovare le istruzioni più lente nel log. Per ciascuno di essi, puoi quindi verificare come viene eseguito dal database e iniziare a migliorarlo. Se la query diventa troppo complessa per JPQL, puoi implementarla come query SQL nativa.

Se Hibernate ha utilizzato troppi batch JDBC o ha trovato solo poche entità nella cache di 2° livello, dovresti controllare il mio Hibernate Performance Tuning Online Training. Questi problemi sono generalmente causati da una semplice configurazione errata o da un malinteso sulla funzione e su come utilizzarla.

E spesso si verificano troppi svuotamenti o tempi di esecuzione lunghi per i tuoi svuotamenti quando utilizzi troppe entità.

PerfTester

Le statistiche di Hibernate forniscono molte informazioni per identificare e risolvere i problemi di prestazioni. Ma trovare il problema e scegliere l'approccio giusto per risolverlo richiede comunque un'analisi dettagliata e molta esperienza.

Ho sviluppato PerfTester per rendere tutto questo molto più semplice. L'obiettivo dello strumento è automatizzare gran parte dell'analisi e delle ottimizzazioni dell'ottimizzazione delle prestazioni che eseguiamo nei miei tipici impegni di coaching.

PerfTester analizza le operazioni interne di Hibernate ed esegue le query del database in fase di esecuzione. In questo modo, trova problemi di prestazioni e inefficienze esistenti. Riporta quale classe e riga di codice lo causano e consiglia approcci diversi per risolverlo.

[N_PLUS_ONE] Potential n+1 select issue detected for association com.thorben.janssen.app.spring.nPlusOne.entity.Author.books. You fetch it for 100% of the loaded com.thorben.janssen.app.spring.nPlusOne.entity.Author.

You should use a JOIN FETCH or an EntityGraph to initialize the association. Learn more at: https://thorben-janssen.com/5-ways-to-initialize-lazy-relations-and-when-to-use-them/

Please review the following places in which you load com.thorben.janssen.app.spring.nPlusOne.entity.Author.books associations:
Query: LoadInfo [query=Initialize association com.thorben.janssen.app.spring.nPlusOne.entity.Author.books, executionPoint=com.thorben.janssen.app.spring.nPlusOne.SpringNPlusOneWarningTestExamples.fail_ManyToMany_lazyAccess_failOnWarning(SpringNPlusOneWarningTestExamples.java:65)]

These are fetched for com.thorben.janssen.app.spring.nPlusOne.entity.Author entities with ids: 1, 2, 3, 4

You load these entities at:
LoadInfo [query=select generatedAlias0 from Author as generatedAlias0, executionPoint=com.thorben.janssen.app.spring.nPlusOne.SpringNPlusOneWarningTestExamples.fail_ManyToMany_lazyAccess_failOnWarning(SpringNPlusOneWarningTestExamples.java:63)]

Ciò velocizza il processo di analisi e fornisce tutte le conoscenze necessarie per risolvere i problemi di prestazioni che causano problemi reali in produzione. Se suona come qualcosa che vuoi usare per il tuo progetto, puoi iscriverti a PerfTester su https://thorben-janssen.com/perftester/.


Etichetta Java