2010-06-12 17 views
15

Estoy trabajando en una parte sensible a la latencia de una aplicación, básicamente recibiré un evento de red para transformar los datos y luego insertar todos los datos en la base de datos. Después del perfilado, veo que básicamente todo mi tiempo lo paso intentando guardar los datos. aquí está el códigoinserto de hibernación de alto rendimiento

private void insertAllData(Collection<Data> dataItems) 
{ 
    long start_time = System.currentTimeMillis(); 
    long save_time = 0; 
    long commit_time = 0; 
    Transaction tx = null; 
    try 
    { 
     Session s = HibernateSessionFactory.getSession(); 
     s.setCacheMode(CacheMode.IGNORE); 
     s.setFlushMode(FlushMode.NEVER); 
     tx = s.beginTransaction(); 
     for(Data data : dataItems) 
     { 
      s.saveOrUpdate(data); 
     } 
     save_time = System.currentTimeMillis(); 
     tx.commit(); 
     s.flush(); 
     s.clear(); 
    } 
    catch(HibernateException ex) 
    { 
     if(tx != null) 
      tx.rollback(); 
    } 
    commit_time = System.currentTimeMillis(); 
    System.out.println("Save: " + (save_time - start_time)); 
    System.out.println("Commit: " + (commit_time - save_time)); 
    System.out.println(); 
} 

El tamaño de la colección es siempre inferior a 20. Aquí está la información de temporización que veo:

Save: 27 
Commit: 9 

Save: 27 
Commit: 9 

Save: 26 
Commit: 9 

Save: 36 
Commit: 9 

Save: 44 
Commit: 0 

Esto es confuso para mí. Me imagino que el save debe ser rápido y todo el tiempo se debe gastar en commit. pero claramente estoy equivocado. También intenté eliminar la transacción (no es realmente necesario) pero vi tiempos peores ... He configurado hibernate.jdbc.batch_size = 20 ...

Puedo esperar obtener hasta 500 mensajes/seg entonces necesito que el manejo de un solo mensaje sea de menos de 20 milisegundos.

necesito que esta operación sea lo más rápida posible, idealmente solo habría una ida y vuelta a la base de datos. ¿Cómo puedo hacer esto?

+0

BTW, ¿no se supone que 'commit() 'después de' flush() 'cuando se usa' FlushMode # NEVER'? –

+0

@Pascal Thivent. No sé :-) – luke

+1

Bien, lea el javadoc de 'Transaction # commit()' :) –

Respuesta

13

Mueva la generación de su llave primaria lejos de un auto-incremento en el lado del servidor. Su código Java debe ser responsable de la generación PK para evitar viajes redondos.

Para obtener un rendimiento de inserción masivo decente, necesita un método que no necesite golpear la base de datos en cada llamada a saveOrUpdate. Usar UUID como la clave principal, o implementing HiLo puede ayudar a lograr esto. De lo contrario, no hay ninguna inserción masiva en realidad.

Para tener tanto el rendimiento como la interoperabilidad con otros sistemas externos, los optimizadores combinados o pooled-lo son la mejor opción.

+1

Actualmente estoy usando una secuencia de oráculo para generar identificadores. ¿esto no es factible? – luke

+1

¡Eso fue exactamente! Eliminé la secuencia y agregué una consulta al inicio para averiguar dónde comenzar la secuencia y bam, con una aceleración de 7.5X que lo ubicaba muy por debajo de mi umbral. – luke

+0

Me alegra oír que funcionó :-) – Michael

3

Honestamente, no sé qué se puede concluir razonablemente de su prueba y de las "medidas" que muestra (sospecho que mucho sobrecarga del calentamiento, la colección es muy pequeña, y la muestra es muy pequeña)

De todos modos, puedo decirles que su código actual no se escalará y es muy probable que vaya a explotar la sesión al pasar una colección más grande. Debe enjuagar y borrar la sesión a intervalos regulares (cada 20 registros si el tamaño del lote es 20).

En realidad, recomiendo leer todo Chapter 13. Batch processing.

+0

Estoy descargando y borrando la sesión en el código anterior. las colecciones nunca serán mayores que 20. – luke

0

Algunas cosas básicas:

  • ¿Tiene desencadenantes, o extranjeros clave limitaciones con ningún índice?
  • ¿Tiene controladores de procesamiento por lotes?
  • ¿Están sus controladores en modo de proceso por lotes (consulte hibernate.jdbc.batch_size a partir de la referencia de Pascal)?
  • Cualquier índice en sus tablas (si tiene muchos índices, a veces puede ralentizar el inserto)?

El procesamiento por lotes es parte de JDBC 2.0, le permite ejecutar varias instrucciones en un 'lote'; la idea es reducir la latencia de ida y vuelta (puede ejecutar varios lotes por transacción).

Statement stmt = dbCon.createStatement("insert into DataTable values (?,?,?)"); 
stmt.setInt(1, x1); stmt.setInt(2, x2), stmt.setString(3, "some value"); 
stmt.addBatch(); 
... 
stmt.setInt(1, x2); stmt.setInt(2, x3), stmt.setString(3, "some other value"); 
stmt.addBatch(); 

stmt.executeBatch(); 
dbCon.commit(); 

Probablemente pueda utilizar esto como una prueba de referencia. También me gustaría ver el SQL que hibernate genera, para ver si está realizando una consulta por inserción para obtener los Ids generados.

+1

¿cómo puedo saber si tengo un controlador de procesamiento por lotes? – luke

Cuestiones relacionadas