Sospensione della dimensione del batch in ibernazione

Questo programma fa decine di migliaia di inserti consecutivi uno dopo l’altro. Non ho mai usato Hibernate prima. Otterrò prestazioni estremamente lente (se mi collego e eseguo l’SQL manualmente, sono 10-12x più veloce. Il mio batch_size è impostato su 50 come per molte esercitazioni di ibernazione.

Ecco un registro da un singolo inserto – forse potresti aiutarmi a capire esattamente cosa sta succedendo:

START INSERT 11:02:56.121 [main] DEBUG org.hibernate.impl.SessionImpl - opened session at timestamp: 13106053761 11:02:56.121 [main] DEBUG ohtransaction.JDBCTransaction - begin 11:02:56.121 [main] DEBUG org.hibernate.jdbc.ConnectionManager - opening JDBC connection 11:02:56.121 [main] TRACE ohcDriverManagerConnectionProvider - total checked-out connections: 0 11:02:56.121 [main] TRACE ohcDriverManagerConnectionProvider - using pooled JDBC connection, pool size: 0 11:02:56.121 [main] DEBUG ohtransaction.JDBCTransaction - current autocommit status: false 11:02:56.121 [main] TRACE org.hibernate.jdbc.JDBCContext - after transaction begin 11:02:56.121 [main] TRACE org.hibernate.impl.SessionImpl - setting flush mode to: MANUAL 11:02:56.121 [main] TRACE ohedef.DefaultLoadEventListener - loading entity: [com.xyzcompany.foo.edoi.ejb.msw000.MSW000Rec#component[keyW000]{keyW000=F000 ADSUFC}] 11:02:56.121 [main] TRACE ohedef.DefaultLoadEventListener - creating new proxy for entity 11:02:56.122 [main] TRACE ohedDefaultSaveOrUpdateEventListener - saving transient instance 11:02:56.122 [main] DEBUG ohedef.AbstractSaveEventListener - generated identifier: component[keyW000]{keyW000=F000 ADSUFC}, using strategy: org.hibernate.id.CompositeNestedGeneratedValueGenerator 11:02:56.122 [main] TRACE ohedef.AbstractSaveEventListener - saving [com.xyzcompany.foo.edoi.ejb.msw000.MSW000Rec#component[keyW000]{keyW000=F000 ADSUFC}] 11:02:56.123 [main] TRACE ohedAbstractFlushingEventListener - flushing session 11:02:56.123 [main] DEBUG ohedAbstractFlushingEventListener - processing flush-time cascades 11:02:56.123 [main] DEBUG ohedAbstractFlushingEventListener - dirty checking collections 11:02:56.123 [main] TRACE ohedAbstractFlushingEventListener - Flushing entities and processing referenced collections 11:02:56.125 [main] TRACE ohedAbstractFlushingEventListener - Processing unreferenced collections 11:02:56.125 [main] TRACE ohedAbstractFlushingEventListener - Scheduling collection removes/(re)creates/updates 11:02:56.126 [main] DEBUG ohedAbstractFlushingEventListener - Flushed: 1 insertions, 0 updates, 0 deletions to 62 objects 11:02:56.126 [main] DEBUG ohedAbstractFlushingEventListener - Flushed: 0 (re)creations, 0 updates, 0 removals to 0 collections 11:02:56.132 [main] TRACE ohedAbstractFlushingEventListener - executing flush 11:02:56.132 [main] TRACE org.hibernate.jdbc.ConnectionManager - registering flush begin 11:02:56.132 [main] TRACE ohpentity.AbstractEntityPersister - Inserting entity: [com.xyzcompany.foo.edoi.ejb.msw000.MSW000Rec#component[keyW000]{keyW000=F000 ADSUFC}] 11:02:56.132 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to open PreparedStatement (open PreparedStatements: 0, globally: 0) 11:02:56.132 [main] DEBUG org.hibernate.SQL - insert into MSW000 (W000_DATA_REC, W000_FILE_FLAGS, KEY_W000) values (?, ?, ?) 11:02:56.132 [main] TRACE org.hibernate.jdbc.AbstractBatcher - preparing statement 11:02:56.132 [main] TRACE ohpentity.AbstractEntityPersister - Dehydrating entity: [com.xyzcompany.foo.edoi.ejb.msw000.MSW000Rec#component[keyW000]{keyW000=F000 ADSUFC}] 11:02:56.132 [main] TRACE org.hibernate.type.StringType - binding ' ADSUFCA ' to parameter: 1 11:02:56.132 [main] TRACE org.hibernate.type.StringType - binding ' ' to parameter: 2 11:02:56.132 [main] TRACE org.hibernate.type.StringType - binding 'F000 ADSUFC' to parameter: 3 11:02:56.132 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - Executing batch size: 1 11:02:56.133 [main] DEBUG org.hibernate.jdbc.AbstractBatcher - about to close PreparedStatement (open PreparedStatements: 1, globally: 1) 11:02:56.133 [main] TRACE org.hibernate.jdbc.AbstractBatcher - closing statement 11:02:56.133 [main] TRACE org.hibernate.jdbc.ConnectionManager - registering flush end 11:02:56.133 [main] TRACE ohedAbstractFlushingEventListener - post flush 11:02:56.133 [main] DEBUG ohtransaction.JDBCTransaction - commit 11:02:56.133 [main] TRACE org.hibernate.impl.SessionImpl - automatically flushing session 11:02:56.133 [main] TRACE org.hibernate.jdbc.JDBCContext - before transaction completion 11:02:56.133 [main] TRACE org.hibernate.impl.SessionImpl - before transaction completion 11:02:56.133 [main] DEBUG ohtransaction.JDBCTransaction - committed JDBC Connection 11:02:56.133 [main] TRACE org.hibernate.jdbc.JDBCContext - after transaction completion 11:02:56.133 [main] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources! 11:02:56.133 [main] TRACE org.hibernate.impl.SessionImpl - after transaction completion 11:02:56.133 [main] TRACE org.hibernate.impl.SessionImpl - closing session 11:02:56.133 [main] TRACE org.hibernate.jdbc.ConnectionManager - performing cleanup 11:02:56.133 [main] DEBUG org.hibernate.jdbc.ConnectionManager - releasing JDBC connection [ (open PreparedStatements: 0, globally: 0) (open ResultSets: 0, globally: 0)] 11:02:56.133 [main] TRACE ohcDriverManagerConnectionProvider - returning connection to pool, pool size: 1 11:02:56.133 [main] TRACE org.hibernate.jdbc.JDBCContext - after transaction completion 11:02:56.133 [main] DEBUG org.hibernate.jdbc.ConnectionManager - transaction completed on session with on_close connection release mode; be sure to close the session to release JDBC resources! 11:02:56.134 [main] TRACE org.hibernate.impl.SessionImpl - after transaction completion FINISH INSERT 

Quando chiami session.save() , hibernate genererà un SQL INSERT. Questo INSERT SQL verrà aggiunto al DB durante lo svuotamento (es. session.flush() ).

Durante lo scarico, se hibernate.jdbc.batch_size è impostato su un valore diverso da zero, Hibernate utilizzerà la funzione di batch introdotta nell’API JDBC2 per emettere l’SQL di inserimento batch nel DB.

Ad esempio, se si save() 100 record e il proprio hibernate.jdbc.batch_size è impostato su 50. Durante lo svuotamento, invece di emettere il seguente SQL 100 volte:

 insert into TableA (id , fields) values (1, 'val1'); insert into TableA (id , fields) values (2, 'val2'); insert into TableA (id , fields) values (3, 'val3'); ......................... insert into TableA (id , fields) values (100, 'val100'); 

Hiberate li raggrupperà in batch di 50 e solo SQL 2 per il DB, in questo modo:

 insert into TableA (id , fields) values (1, 'val1') , (2, 'val2') ,(3, 'val3') ,(4, 'val4') ,......,(50, 'val50') insert into TableA (id , fields) values (51, 'val51') , (52, 'val52') ,(53, 'val53') ,(54, 'val54'),...... ,(100, 'val100') 

Si noti che Hibernate disabilita in modo trasparente il batch di inserimento a livello di JDBC se la chiave primaria della tabella di inserimento è GenerationType.Identity .

Dal tuo log: save() solo un record e poi flush() , quindi c’è un solo SQL INSERT accodato da elaborare per ogni colore. Ecco perché Hibernate non può aiutarti a inserire in batch poiché è necessario elaborare un solo SQL INSERT. Dovresti save() fino alla certa quantità di record prima di chiamare flush() invece di chiamare flush() per ogni save() .

La migliore pratica di inserimento in batch è qualcosa del genere:

 Session session = sessionFactory.openSession(); Transaction tx = session.beginTransaction(); for ( int i=0; i<888888; i++ ) { TableA record = new TableA(); record.setXXXX(); session.save(record) if ( i % 50 == 0 ) { //50, same as the JDBC batch size //flush a batch of inserts and release memory: session.flush(); session.clear(); } } tx.commit(); session.close(); 

Si salvano e si scaricano i record batch per batch. Alla fine di ogni batch è necessario cancellare il contesto di persistenza per liberare memoria per evitare l'esaurimento della memoria poiché ogni object persistente viene inserito nella cache di primo livello (la memoria della JVM). È inoltre ansible disabilitare la cache di secondo livello per ridurre l'overhead non necessario.


Riferimento:

  • Documentazione ufficiale di Hibernate: Capitolo 14. Elaborazione batch
  • Hibernate Batch Processing - Perché potresti non utilizzarlo. (Anche se pensi di esserlo)

Se devi usare l’ibernazione per enormi lavori batch, StatelessSession è la strada da percorrere. Riduce le cose alla più semplice mapping di converting-objects-to-SQL ed elimina tutto il sovraccarico delle funzionalità ORM che non state utilizzando quando si riempiono le righe nel DB all’ingrosso.

Sarebbe anche molto più semplice dare suggerimenti sul tuo codice reale rispetto al registro 🙂

 11:02:56.133 [main] DEBUG ohtransaction.JDBCTransaction - commit 

Questo sta dicendo che il database si sta impegnando dopo ogni inserimento. Assicurati di non commettere la tua transazione / chiudere la sessione all’interno del ciclo di inserimento. Fai questo una volta alla fine, invece.