2011-09-08 23 views
15

Necesito consumir una gran cantidad de datos de un archivo CSV diario. El archivo CSV contiene alrededor de 120,000 registros. Esto se ralentiza a paso de tortuga cuando se utiliza hibernación. Básicamente, parece que hibernate está haciendo un SELECT antes de cada INSERT (o UPDATE) al usar saveOrUpdate(); para cada instancia que persista con saveOrUpdate(), se emite un SELECT antes del INSERT real o una ACTUALIZACIÓN. Puedo entender por qué está haciendo esto, pero es terriblemente ineficiente para hacer un procesamiento masivo, y estoy buscando alternativasInserción masiva O actualización con hibernación?

Estoy seguro de que el problema de rendimiento reside en la forma en que estoy usando Hibernate para esto, ya que tiene otra versión que funciona con SQL nativo (que analiza el CSV de la misma manera) y que literalmente ejecuta círculos alrededor de esta nueva versión)

Entonces, para la pregunta real, ¿una alternativa de hibernación a mysqls "INSERTAR ... ON DUPLICATE "¿existe sintaxis?

O, si elijo hacer SQL nativo para esto, ¿puedo hacer SQL nativo dentro de una transacción de hibernación? Es decir, ¿admitirá commit/rollbacks?

+0

¿qué es lo que quiere decir con "hibernación está haciendo un SELECT antes de cada inserción individual (o actualización) cuando se utiliza saveOrUpdate()". ?¿podría publicar el código que está utilizando para guardar los datos? por cierto, 120k registros es una gran cantidad de datos! – Rakesh

+0

Acabo de encontrar un artículo sobre [procesamiento por lotes en hibernación] (http://onetouchcode.com/2016/08/21/batch-processing-example-in-hibernate/) – Shailendra

Respuesta

22

Hay muchos posibles cuellos de botella en operaciones masivas. El mejor enfoque depende en gran medida de cómo se ve tu información. Eche un vistazo a la sección Hibernate Manual sobre procesamiento por lotes.

Como mínimo, asegúrese de que está utilizando el siguiente patrón (copiado del manual):

Session session = sessionFactory.openSession(); 
Transaction tx = session.beginTransaction(); 

for (int i=0; i<100000; i++) { 
Customer customer = new Customer(.....); 
session.save(customer); 
    if (i % 20 == 0) { //20, same as the JDBC batch size 
     //flush a batch of inserts and release memory: 
     session.flush(); 
     session.clear(); 
    } 
} 

tx.commit(); 
session.close(); 

Si va a asignar un archivo plano a un gráfico de objetos muy complejo que puede tener que ser más creativos , pero el principio básico es que debe encontrar un equilibrio entre enviar pedazos de datos de buen tamaño a la base de datos con cada descarga/confirmación y evitar explotar el tamaño de la memoria caché de nivel de sesión.

Por último, si no necesita Hibernate para manejar cualquier colección o en cascada para que sus datos se inserten correctamente, considere usar un StatelessSession.

+0

Estoy descargando un claro de mi sesión, no tengo problemas de memoria con el código. Tengo problemas con la selección extra! : P Leo el manual, no puedo encontrar nada. La información es súper simple, no se necesita una cascada. Solo necesito deshacerme de, para esta tarea, la selección redundante que se llama 120,000 veces: P – JustDanyul

+0

@JustDanyul ¿cuál es el porcentaje aproximado de nuevas entidades en esta operación (es decir, qué porcentaje de las selectas son realmente innecesarias)? ¿Estás usando versiones? – jcwayne

+0

el porcentaje real variará día a día. Sin embargo, ninguno de los seleccionados debería ser realmente necesario. La mayoría de las bases de datos de hoy (incluso las de "juguete" como SQLite) proporcionan funcionalidades que le permitirán actualizar automáticamente un registro si los datos ya existen. (sin tener que sondear primero, para saber si existe :)) – JustDanyul

0

La selección "extra" es generar el identificador único para sus datos.

Cambie a la generación de secuencia HiLo y puede reducir la secuencia de ida y vuelta a la base de datos por el número de tamaño de asignación. Tenga en cuenta, habrá un vacío en las claves principales a menos que ajuste el valor de secuencia para el generador alto

1

Si utiliza la secuencia o generador nativo Hibernate utilizará una selección para obtener el ID:

<id name="id" column="ID"> 
    <generator class="native" /> 
</id> 

Usted debe usar hilo o generador seqhilo:

<id name="id" type="long" column="id"> 
    <generator class="seqhilo"> 
     <param name="sequence">SEQ_NAME</param> 
     <param name="max_lo">100</param> 
    </generator> 
</id> 
3

De Hibernate Batch Processing Para la actualización he usado el siguiente:

Session session = sessionFactory.openSession(); 
Transaction tx = session.beginTransaction(); 

ScrollableResults employeeCursor = session.createQuery("FROM EMPLOYEE") 
            .scroll(); 
int count = 0; 

while (employeeCursor.next()) { 
    Employee employee = (Employee) employeeCursor.get(0); 
    employee.updateEmployee(); 
    seession.update(employee); 
    if (++count % 50 == 0) { 
     session.flush(); 
     session.clear(); 
    } 
} 
tx.commit(); 
session.close(); 

Pero para insertar Yo iría por jcwayne respuesta

Cuestiones relacionadas