2012-01-20 24 views
5

Tener problema de rendimiento extraño usando Hibernate 3.3.2GA detrás de la APP (y el resto de los paquetes incluidos en Hibernate JBoss 5.)APP (Hibernate) Nativo de consulta para la Declaración preparada LENTO

que estoy usando nativo de consulta y ensamblando SQL en una declaración preparada.

EntityManager em = getEntityManager(MY_DS); 
final Query query = em.createNativeQuery(fullSql, entity.getClass()); 

El SQL tiene muchas uniones, pero en realidad es muy básico, con un solo parámetro. Me gusta:

SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on 
WHERE stringId like ? 

y la consulta se ejecuta en menos de un segundo en MSSQL Studio.

Si añado

query.setParameter(0, "ABC123%"); 

La consulta se detienen durante 9 segundos

2012-01-20 14:36:21 - TRACE: - AbstractBatcher.getPreparedStatement:(484) | preparing statement 
2012-01-20 14:36:21 - TRACE: - StringType.nullSafeSet:(133) | binding 'ABC123%' to parameter: 1 
2012-01-20 14:36:30 - DEBUG: - AbstractBatcher.logOpenResults:(382) | about to open ResultSet (open ResultSets: 0, globally: 0) 

Sin embargo, si sólo se sustituye la "?" con el valor (lo que no es una declaración preparada, pero sólo una consulta SQL recta.

fullSql = fullSql.replace("?", "'ABC123%'"); 

la consulta completará en menos de un segundo.

que realmente preferiría que nos una declaración preparada (la entrada para los parámetros se está extrayendo de los datos del usuario) para prevenir ataques de inyección.

rastreando el punto lento en el código, llegué profundamente dentro del paquete jtds-1.2.2. La línea ofensiva parece ser SharedSocket línea 841 "getIn(). readFully (hdrBuf);" Nada realmente obvio allí ...

private byte[] readPacket(byte buffer[]) 
     throws IOException { 
    // 
    // Read rest of header 
    try { 
     getIn().readFully(hdrBuf); 
    } catch (EOFException e) { 
     throw new IOException("DB server closed connection."); 
    } 

Llegados a través de esta pila ...

at net.sourceforge.jtds.jdbc.SharedSocket.readPacket(SharedSocket.java:841) 
    at net.sourceforge.jtds.jdbc.SharedSocket.getNetPacket(SharedSocket.java:722) 
    at net.sourceforge.jtds.jdbc.ResponseStream.getPacket(ResponseStream.java:466) 
    at net.sourceforge.jtds.jdbc.ResponseStream.read(ResponseStream.java:103) 
    at net.sourceforge.jtds.jdbc.ResponseStream.peek(ResponseStream.java:88) 
    at net.sourceforge.jtds.jdbc.TdsCore.wait(TdsCore.java:3928) 
    at net.sourceforge.jtds.jdbc.TdsCore.executeSQL(TdsCore.java:1045) 
    at net.sourceforge.jtds.jdbc.TdsCore.microsoftPrepare(TdsCore.java:1178) 
    at net.sourceforge.jtds.jdbc.ConnectionJDBC2.prepareSQL(ConnectionJDBC2.java:657) 
    at net.sourceforge.jtds.jdbc.JtdsPreparedStatement.executeQuery(JtdsPreparedStatement.java:776) 
    at org.hibernate.jdbc.AbstractBatcher.getResultSet(AbstractBatcher.java:208) 
    at org.hibernate.loader.Loader.getResultSet(Loader.java:1808) 
    at org.hibernate.loader.Loader.doQuery(Loader.java:697) 
    at org.hibernate.loader.Loader.doQueryAndInitializeNonLazyCollections(Loader.java:259) 
    at org.hibernate.loader.Loader.doList(Loader.java:2228) 
    at org.hibernate.loader.Loader.listIgnoreQueryCache(Loader.java:2125) 
    at org.hibernate.loader.Loader.list(Loader.java:2120) 
    at org.hibernate.loader.custom.CustomLoader.list(CustomLoader.java:312) 
    at org.hibernate.impl.SessionImpl.listCustomQuery(SessionImpl.java:1722) 
    at org.hibernate.impl.AbstractSessionImpl.list(AbstractSessionImpl.java:165) 
    at org.hibernate.impl.SQLQueryImpl.list(SQLQueryImpl.java:175) 
    at org.hibernate.ejb.QueryImpl.getResultList(QueryImpl.java:67) 
+0

Déjenme agregar jtds-1.2.2 a la pila de tecnología. He depurado a través de Hibernate en JTDS – javatestcase

+0

intenté jtds-1.2.4, pero no me alegré ... – javatestcase

+0

Cambiando a com.microsoft.sqlserver.jdbc.SQLServerDriver en realidad produce resultados idénticos ... Tengo que echar un vistazo a SQL Server, tal vez algo allí ... – javatestcase

Respuesta

8

Dejaré esta pregunta y la responderé aquí en caso de que alguien tenga el mismo problema en el futuro.

El problema está en la forma en que los controladores JTDS envían las cadenas de parámetros a MSSQL. Aparentemente, Java intentará enviar los parámetros Unicode de forma predeterminada, y MSSQL lo traducirá a Ascii. Por qué eso toma 9 segundos, no lo sé.

Hay un montón de referencias a esto, pero nada que me ayudara hasta que pude aislar que era un problema con el controlador para la conexión MSSQL.

Este enlace fue útil:

[http://server.pramati.com/blog/2010/06/02/perfissues-jdbcdrivers-mssqlserver/]

Ésta es la cadena usando el Microsoft conductor.

jdbc:sqlserver://localhost\SQLEXPRESS; 
    DatabaseName=TESTDB; 
    sendStringParametersAsUnicode=false 

sólo tiene que conseguir el sendStringParametersAsUnicode = false pasado a la configuración de URL conductor y que son buenos.

+8

Toma 9 segundos porque no convierte su parámetro a ascii (ya que podría perder datos de esa manera). Cambia el valor de cada columna a unicode antes de comparar con tu parámetro. Esto significa que no puede sacar el máximo provecho de ningún índice en el campo "stringId", lo que lleva a un rendimiento mucho más lento (creo que también tuve un problema como este en el pasado). – Gareth

+0

Por cierto, estoy feliz de que hayas resuelto tu problema. – Gareth

+0

gracias. tu explicación realmente ayuda a entender el problema. – javatestcase

3

Comprobar la consulta tiene previsto que el servidor SQL está produciendo. Las declaraciones preparadas pueden ser especialmente problemáticas.

Me explico ...

Si hace esto:

SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on 
WHERE stringId like 'ABC123%'; 

y tiene un índice en "stringID" servidor SQL sabe que puede utilizarlo.

Sin embargo, si usted hace esto:

SELECT field1, field2, field3 FROM entity left join entity2 on... left join entity3 on 
WHERE stringId like ?; 

servidor SQL no sabe que puede utilizar el índice cuando se crea la declaración preparada (como se puede rellenar con el parámetro '% ABC123' en lugar de ' ABC123% ') y, por lo tanto, puede elegir un plan de consulta completamente diferente.

+0

Gracias Gareth. Lo que dices es 100% verdadero. En este caso, el problema principal es con la conexión del controlador-MSSQL. Creo que MSSQL puede almacenar en caché los planes de consulta, y en realidad solo hay unas pocas iteraciones posibles del SP, con diferentes subconsultas para relaciones de uno a muchos. Después de solucionar ese problema, 100 consultas vuelven en 47 segundos, ¡una gran mejora de 9 segundos cada una! – javatestcase

1

Y otra respuesta para las personas que utilizan Oracle potencialmente un problema con Unicode similares ...

comprobación para asegurarse de que alguien no ha determinado la propiedad oracle.jdbc.defaultNChar = true

Esto se hace a veces para resolver problemas Unicode pero significa que todas las columnas se tratan como nvarchars. Si tiene un índice en una columna varchar, no se usará porque Oracle tiene que usar una función para convertir la codificación de caracteres.

Cuestiones relacionadas