Domanda

Ho bisogno di esportare grandi quantità di dati dal database. Ecco classi che rappresenta il mio dati:

public class Product{
...

    @OneToMany
    @JoinColumn(name = "product_id")
    @Cascade({SAVE_UPDATE, DELETE_ORPHAN})
    List<ProductHtmlSource> htmlSources = new ArrayList<ProductHtmlSource>();

...    }

ProductHtmlSource - contiene grandi stringa all'interno del quale ho effettivamente bisogno di esportare.

Dal dimensione dei dati esportati è più grande di memoria JVM che sto leggendo i miei dati per blocchi. In questo modo:

final int batchSize = 1000;      
for (int i = 0; i < 50; i++) {
  ScrollableResults iterator = getProductIterator(batchSize * i, batchSize * (i + 1));
  while (iterator.getScrollableResults().next()) {
     Product product = (Product) iterator.getScrollableResults().get(0); 
     List<String> htmls = product.getHtmlSources();
     <some processing>
  }

}

Codice getProductIterator:

public ScrollableResults getProductIterator(int offset, int limit) {
        Session session = getSession(true);
        session.setCacheMode(CacheMode.IGNORE);
        ScrollableResults iterator = session
                .createCriteria(Product.class)
                .add(Restrictions.eq("status", Product.Status.DONE))
                .setFirstResult(offset)
                .setMaxResults(limit)
                .scroll(ScrollMode.FORWARD_ONLY);
        session.flush();
        session.clear();

        return iterator;
    }

Il problema è che, nonostante io compensazione sessione dopo la lettura dei dati di ogni chunk oggetti Product accumula qualche parte e io sono ottengo un'eccezione OutOfMemory. Il problema non è in blocco di elaborazione del codice, anche senza di essa ottengo l'errore di memoria. La dimensione del lotto non è un problema fin dal 1000 oggetti facilmente sedersi in memoria.

Profiler ha dimostrato che gli oggetti si accumula in classe org.hibernate.engine.StatefulPersistenceContext.

Lo stacktrace:

Caused by: java.lang.OutOfMemoryError: Java heap space
    at java.lang.AbstractStringBuilder.expandCapacity(AbstractStringBuilder.java:99)
    at java.lang.AbstractStringBuilder.append(AbstractStringBuilder.java:518)
    at java.lang.StringBuffer.append(StringBuffer.java:307)
    at org.hibernate.type.TextType.get(TextType.java:41)
    at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:163)
    at org.hibernate.type.NullableType.nullSafeGet(NullableType.java:154)
    at org.hibernate.type.AbstractType.hydrate(AbstractType.java:81)
    at org.hibernate.persister.entity.AbstractEntityPersister.hydrate(AbstractEntityPersister.java:2101)
    at org.hibernate.loader.Loader.loadFromResultSet(Loader.java:1380)
    at org.hibernate.loader.Loader.instanceNotYetLoaded(Loader.java:1308)
    at org.hibernate.loader.Loader.getRow(Loader.java:1206)
    at org.hibernate.loader.Loader.getRowFromResultSet(Loader.java:580)
    at org.hibernate.loader.Loader.doQuery(Loader.java:701)
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:236)
    at org.hibernate.loader.Loader.loadCollection(Loader.java:1994)
    at org.hibernate.loader.collection.CollectionLoader.initialize(CollectionLoader.java:36)
    at org.hibernate.persister.collection.AbstractCollectionPersister.initialize(AbstractCollectionPersister.java:565)
    at org.hibernate.event.def.DefaultInitializeCollectionEventListener.onInitializeCollection(DefaultInitializeCollectionEventListener.java:63)
    at org.hibernate.impl.SessionImpl.initializeCollection(SessionImpl.java:1716)
    at org.hibernate.collection.AbstractPersistentCollection.initialize(AbstractPersistentCollection.java:344)
    at org.hibernate.collection.AbstractPersistentCollection.read(AbstractPersistentCollection.java:86)
    at org.hibernate.collection.AbstractPersistentCollection.readSize(AbstractPersistentCollection.java:109)
    at org.hibernate.collection.PersistentBag.size(PersistentBag.java:225)
    **at com.rivalwatch.plum.model.Product.getHtmlSource(Product.java:76)
    at com.rivalwatch.plum.model.Product.getHtmlSourceText(Product.java:80)
    at com.rivalwatch.plum.readers.AbstractDataReader.getData(AbstractDataReader.java:64)**
È stato utile?

Soluzione

Sembra che si sta chiamando getProductIterator () con inizio e fine numeri di riga, mentre getProductIterator () si aspetta la riga di partenza e un conteggio delle righe. Come il vostro "limite superiore" diventa più alto si sta leggendo i dati in blocchi più grandi. Credo che si intende far passare batchSize come secondo argomento di getProductIterator ().

Altri suggerimenti

Non è una risposta diretta, ma per questo tipo di manipolazione dei dati, vorrei utilizzare l'interfaccia StatelessSession .

KeithL è giusto - si sta passando un limite sempre crescente. Ma la rottura in su in questo modo non ha senso comunque. Il punto centrale di un cursore di scorrimento è che si elabora una riga alla volta quindi non c'è bisogno di suddividerlo in pezzi. La dimensione di recupero riduce i viaggi al database al costo di utilizzo di più memoria. Lo schema generale dovrebbe essere:

Query q = session.createCriteria(... no offset or limit ...);
q.setCacheMode(CacheMode.IGNORE); // prevent query or second level caching
q.setFetchSize(1000);  // experiment with this to optimize performance vs. memory
ScrollableResults iterator = query.scroll(ScrollMode.FORWARD_ONLY);
while (iterator.next()) {
  Product p = (Product)iterator.get();
  ...
  session.evict(p);  // required to keep objects from accumulating in the session
}

Detto questo, l'errore è getHtmlSources in modo che il problema può essere completamente estranei alla questione della sessione / cursore / scorrimento. Se queste stringhe html sono enormi e sono in fase di riferimento per tutto il tempo, si può solo essere a corto di memoria contigua.

A proposito, non vedo un metodo getScrollableResults su ScrollableResults.

A rischio di apparire stupido - avete considerato a fare questo in un altro modo

?

Personalmente vorrei evitare di fare l'elaborazione batch che "lontano" dal database. Non so che cosa database che si sta utilizzando, ma di solito c'è un meccanismo per tirare in modo efficiente un set di dati fuori il database e in un file, anche se si tratta moderatamente semplice manipolazione sulla via d'uscita. Le stored procedure, programmi di utilità specifiche di esportazione. Indagare cos'altro è disponibile presso il vostro fornitore di database.

Puoi pubblicare lo stacktrace eccezione? Può essere risolto passando adatti opzioni JVM per GC.

Credo che questo è legato -. Java StringBuilder testa enorme

appare dal StackTrace che una stringa di grandi dimensioni è stato creato e causando l'eccezione.

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top