Pregunta

Necesito exportar gran cantidad de datos de la base de datos. Aquí es clases que representa mis datos:

public class Product{
...

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

...    }

ProductHtmlSource - contiene gran cadena dentro de la cual realmente se necesita para exportar.

Dado que el tamaño de los datos exportados es más grande que la memoria JVM estoy leyendo mis datos por trozos. De esta manera:

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>
  }

}

Código de 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;
    }

El problema es que a pesar de que la limpieza de la sesión después de leer los datos de cada trozo objetos Product acumula en alguna parte y estoy consigo excepción OutOfMemory. El problema no está en el bloque de procesamiento de código, incluso sin que me sale error de memoria. El tamaño del lote no es también un problema, ya que se sientan 1.000 objetos fácilmente en la memoria.

Profiler mostró que los objetos se acumulan en la clase org.hibernate.engine.StatefulPersistenceContext.

El 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)**
¿Fue útil?

Solución

Parece que está llamando getProductIterator () con el inicial y final de la fila, mientras que getProductIterator () está esperando la fila de partida y un recuento de filas. A medida que su "límite superior" se hace mayor que está leyendo datos en fragmentos más grandes. Creo que te refieres a pasar batchSize como segundo argumento a getProductIterator ().

Otros consejos

No es una respuesta directa, pero para esta clase de manipulación de datos, me gustaría utilizar la interfaz StatelessSession .

KeithL es correcto - estás pasando un límite cada vez mayor. Pero romperlo de esa manera no tiene sentido de todos modos. El punto entero de un cursor de desplazamiento es que procesa una fila a la vez lo que no hay necesidad de dividirlo en trozos. El tamaño de recuperación reduce los viajes a la base de datos en el costo de utilizar más memoria. El patrón general debe ser:

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
}

Dicho esto, el error es getHtmlSources por lo que el problema puede no tener ninguna relación con la cuestión de sesión / cursor / desplazamiento. Si esas cadenas HTML son enormes y están siendo referenciado todo el tiempo, es posible que sólo se esté acabando la memoria contigua.

Por cierto, no veo un método getScrollableResults en ScrollableResults.

A riesgo de parecer estúpida - ¿ha considerado hacer esto de otra manera

?

En lo personal me gustaría evitar hacer el procesamiento por lotes que "lejos" de la base de datos. No sé lo que la base de datos que está utilizando, pero por lo general hay un mecanismo eficiente para tirar de un conjunto de datos de la base de datos y en un archivo, incluso si se trata de la manipulación moderadamente simple en la salida. Los procedimientos almacenados, utilidades de exportación específicos. Investigar lo que hay disponible de su proveedor de base de datos.

Se puede publicar el StackTrace Excepción? Puede ser resuelto por pasar opciones de JVM adecuados para GC.

Creo que esto está relacionado -. Java StringBuilder enorme sobrecarga

Se ve desde el StackTrace que una gran cadena que se está creando y haciendo que la excepción.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top