java - mssql - sql jdbc jar
Rendimiento de Hibernate, JDBC y Java en conjuntos de resultados medianos y grandes (1)
¿Puedes hacer una prueba de humo con la simple consulta posible como:
SELECT current_timestamp()
o
SELECT 1 + 1
Esto le dirá cuál es la sobrecarga real del controlador JDBC. Tampoco está claro si ambas pruebas se realizan desde la misma máquina.
¿Hay una manera de optimizar el rendimiento del controlador JDBC?
Ejecute la misma consulta varios miles de veces en Java. La JVM necesita algo de tiempo para calentarse (carga de clase, JIT). También supongo que SimpleJDBC.getConnection()
utiliza la agrupación de conexiones C3P0: el costo de establecer una conexión es bastante alto, por lo que la primera ejecución podría ser lenta.
También prefiera consultas nombradas a consultas ad-hoc o consultas de criterios.
¿Y Hibernate beneficiará esta optimización?
Hibernate es un marco muy complejo. Como puede ver, consume el 75% del tiempo total de ejecución en comparación con el JDBC sin procesar. Si necesita ORM sin procesar (sin carga mybatis , comprobación sucia, almacenamiento en caché avanzado), considere mybatis . O tal vez incluso JdbcTemplate
con la abstracción RowMapper
.
¿Hay alguna forma de optimizar el rendimiento de Hibernate al convertir conjuntos de resultados?
Realmente no. Consulte el Capítulo 19. Mejora del rendimiento en la documentación de Hibernate. Hay mucha reflexión pasando por ahí + generación de clase. Una vez más, es posible que Hibernate no sea la mejor solución cuando desee exprimir cada milisegundo de su base de datos.
Sin embargo , es una buena opción cuando desea aumentar la experiencia general del usuario debido al amplio soporte de almacenamiento en caché. Echa un vistazo a la documentación de rendimiento de nuevo. En su mayoría habla de almacenamiento en caché. Hay una memoria caché de primer nivel, una memoria caché de segundo nivel, una memoria caché de consulta ... Este es el lugar donde Hibernate podría realmente superar a JDBC simple: puede almacenar mucha memoria caché de una forma que ni siquiera podría imaginar. Por otro lado, una configuración de caché deficiente podría llevar a una configuración aún más lenta.
Echa un vistazo a: Caché con Hibernate + Spring - ¡Algunas preguntas!
¿Estamos frente a algo que no se puede ajustar debido a la gestión de memoria y objeto fundamental de Java?
JVM (especialmente en la configuración del servidor ) es bastante rápido. La creación de objetos en el montón es tan rápida como en la pila, por ejemplo, en C, la recolección de basura se ha optimizado en gran medida. No creo que la versión Java que ejecuta JDBC sea mucho más lenta en comparación con una conexión más nativa. Es por eso que sugerí algunas mejoras en su punto de referencia.
¿Estamos perdiendo un punto, somos estúpidos y todo esto es vano?
Creo que JDBC es una buena opción si el rendimiento es su mayor problema. Java se ha utilizado con éxito en muchas aplicaciones de bases de datos.
Problema
Estamos tratando de optimizar nuestra aplicación de servidor de datos. Almacena acciones y cotizaciones sobre una base de datos mysql. Y no estamos satisfechos con las actuaciones de captación.
Contexto
- database
- table stock : around 500 lines
- table quote : 3 000 000 to 10 000 000 lines
- one-to-many association : one stock owns n quotes
- fetching around 1000 quotes per request
- there is an index on (stockId,date) in the quote table
- no cache, because in production, querys are always different
- Hibernate 3
- mysql 5.5
- Java 6
- JDBC mysql Connector 5.1.13
- c3p0 pooling
Pruebas y resultados
Protocolo
- Los tiempos de ejecución en el servidor mysql se obtienen al ejecutar las consultas de SQL generadas en la línea de comandos mysql.
- El servidor está en un contexto de prueba: no hay otras lecturas de DB, no hay escrituras de DB
- Obtenemos 857 cotizaciones para el stock de AAPL.
Caso 1: Hibernar con asociación.
Esto llena nuestro objeto de stock con 857 comillas objeto (todo correctamente asignado en hibernate.xml)
session.enableFilter("after").setParameter("after", 1322910573000L);
Stock stock = (Stock) session.createCriteria(Stock.class).
add(Restrictions.eq("stockId", stockId)).
setFetchMode("quotes", FetchMode.JOIN).uniqueResult();
SQL generado:
SELECT this_.stockId AS stockId1_1_,
this_.symbol AS symbol1_1_,
this_.name AS name1_1_,
quotes2_.stockId AS stockId1_3_,
quotes2_.quoteId AS quoteId3_,
quotes2_.quoteId AS quoteId0_0_,
quotes2_.value AS value0_0_,
quotes2_.stockId AS stockId0_0_,
quotes2_.volume AS volume0_0_,
quotes2_.quality AS quality0_0_,
quotes2_.date AS date0_0_,
quotes2_.createdDate AS createdD7_0_0_,
quotes2_.fetcher AS fetcher0_0_
FROM stock this_
LEFT OUTER JOIN quote quotes2_ ON this_.stockId=quotes2_.stockId
AND quotes2_.date > 1322910573000
WHERE this_.stockId=''AAPL''
ORDER BY quotes2_.date ASC
Resultados:
- Tiempo de ejecución en el servidor mysql: ~ 10 ms
- Tiempo de ejecución en Java: ~ 400ms
Caso 2: Hibernar sin asociación sin HQL
Pensando en aumentar el rendimiento, hemos utilizado ese código que solo recupera los objetos de cotizaciones y los agregamos manualmente a un stock (por lo que no obtenemos información repetida sobre el stock para cada línea). Utilizamos createSQLQuery para minimizar los efectos de los alias y el desorden HQL.
String filter = " AND q.date>1322910573000";
filter += " ORDER BY q.date DESC";
Stock stock = new Stock(stockId);
stock.addQuotes((ArrayList<Quote>) session.createSQLQuery("select * from quote q where stockId=''" + stockId + "'' " + filter).addEntity(Quote.class).list());
SQL generado:
SELECT *
FROM quote q
WHERE stockId=''AAPL''
AND q.date>1322910573000
ORDER BY q.date ASC
Resultados:
- Tiempo de ejecución en el servidor mysql: ~ 10 ms
- Tiempo de ejecución en Java: ~ 370ms
Caso 3: JDBC sin hibernación
String filter = " AND q.date>1322910573000";
filter += " ORDER BY q.date DESC";
Stock stock = new Stock(stockId);
Connection conn = SimpleJDBC.getConnection();
Statement stmt = conn.createStatement();
ResultSet rs = stmt.executeQuery("select * from quote q where stockId=''" + stockId + "'' " + filter);
while(rs.next())
{
stock.addQuote(new Quote(rs.getInt("volume"), rs.getLong("date"), rs.getFloat("value"), rs.getByte("fetcher")));
}
stmt.close();
conn.close();
Resultados:
- Tiempo de ejecución en el servidor mysql: ~ 10 ms
- Tiempo de ejecución en Java: ~ 100ms
Nuestros entendimientos
- El driver JDBC es común a todos los casos.
- Hay un costo de tiempo fundamental en la conducción JDBC
- Con consultas de SQL similares, Hibernate pasa más tiempo que el código JDBC puro para convertir conjuntos de resultados en objetos
- Hibernate createCriteria, createSQLQuery o createQuery son similares en costo de tiempo
- En la producción, donde tenemos mucha escritura simultánea, la solución JDBC pura parecía ser más lenta que la de hibernación (tal vez porque nuestras soluciones JDBC no estaban agrupadas)
- Mysql sabio, el servidor parece comportarse muy bien, y el coste del tiempo es muy aceptable
Nuestras preguntas
- ¿Hay una manera de optimizar el rendimiento del controlador JDBC?
- ¿Y Hibernate beneficiará esta optimización?
- ¿Hay alguna forma de optimizar el rendimiento de Hibernate al convertir conjuntos de resultados?
- ¿Estamos frente a algo que no se puede ajustar debido a la gestión de memoria y objeto fundamental de Java?
- ¿Estamos perdiendo un punto, somos estúpidos y todo esto es vano?
- ¿Somos franceses? Sí.
Su ayuda es muy bienvenida.