2012-01-04 18 views
5

Intento resolver un problema de rendimiento de mi aplicación. La consulta de hibernación genera, es de la forma:Consulta de la aplicación Hibernate no utiliza índices DB

select * 
from ( 
    select this.a, this.b, this.state, this.id 
    from view_user this 
    where this.state=:1 order by this.a asc, this.b 
) 
where rownum <= :2 

donde

  • id es la clave primaria
  • hay un combinado, índice único en (a, b, id).
  • view_user tiene ~ 2 millones de entradas
  • view_user realiza alguna une más a otras tablas

Edición

La consulta anterior realiza - rápido de SQLDeveloper - rápida de una pequeña aplicación Java con hibernación - extremadamente lento (> 100x más lento) de la aplicación con hibernación - los valores para las variables de vinculación son 2 respectivamente 30 (orígenes de rownum de paginación) : la consulta de hibernación es "de la forma" anterior. En realidad, hay alrededor de 20 columnas en la vista.

estado actual del análisis

  • plan de consulta muestra que el índice se utiliza cuando la consulta proviene de SQLDeveloper o "aplicación de java pequeña".
  • plan de consulta muestra que se realizan exploraciones de tablas completas si la consulta proviene de la aplicación de hibernación
  • El rastreo de DB muestra solo dos diferencias: configuraciones de NLS (de SQLDeveloper) y formatos ligeramente diferentes (espacios en blanco). Todo lo demás parece ser la misma ...

Versiones

  • de hibernación: 2.1.8
  • controlador JDBC: Usados ​​ojdbc14, 5 y 6. hace ninguna diferencia
  • Oracle : 10.2 y 11. No hace ninguna diferencia

=> Me alegro de cualquier pista que alguien pueda tener sobre este tema. Lo que me preocupa es el hecho de que el seguimiento DB no mostró ninguna diferencia ... Sí, parece que se trata de hibernación. ¿Pero que? ¿Cómo detectar?


En aras de la exhaustividad, aquí la consulta de hibernación (desde el registro):

Select * from ( 
    select this.USER_ID as USER_ID0_, this.CLIENT_ID as CLIENT_ID0_, 
    this.USER_NAME as USER_NAME0_, this.USER_FIRST_NAME as USER_FIR5_0_, this.USER_REMARKS as 
    USER_REM6_0_, this.USER_LOGIN_ID as USER_LOG7_0_, this.USER_TITLE as USER_TITLE0_, 
    this.user_language_code as user_lan9_0_, this.USER_SEX as USER_SEX0_, 
    this.USER_BIRTH_DATE as USER_BI11_0_, this.USER_TELEPHONE as USER_TE12_0_, 
    this.USER_TELEFAX as USER_TE13_0_, this.USER_MOBILE as USER_MO14_0_, 
    this.USER_EMAIL as USER_EMAIL0_, this.USER_ADDRESSLINE1 as USER_AD16_0_, 
    this.USER_ADDRESSLINE2 as USER_AD17_0_, this.USER_POSTALCODE as USER_PO18_0_, 
    this.USER_CITY as USER_CITY0_, this.USER_COUNTRY_CD as USER_CO20_0_, 
    this.USER_COUNTRY_NAME as USER_CO21_0_, this.USER_STATE_ID as USER_ST24_0_, 
    this.USER_STATE as USER_STATE0_, this.USER_TEMP_COLL_ID as USER_TE26_0_, 
    this.USER_TEMP_COLL_NAME as USER_TE27_0_, this.UNIT_ID as UNIT_ID0_, 
    this.CLIENT_NAME as CLIENT_38_0_, this.PROFILE_EXTID as PROFILE39_0_ 
    from VIEW_USER this 
    where this.USER_STATE_ID=:1 order by this.USER_NAME asc, this.USER_FIRST_NAME asc 
) 
where rownum <= :2 

índice único es más nombre_usuario, user_first_name, user_id.

+0

Sospecho que tiene un error tipográfico en su consulta que ha introducido aquí, ya que la selección interna tiene dos 'donde' están en él. ¿La cláusula 'where rownum' en realidad está en la selección externa? – rejj

+1

Usted dice que "view_user tiene ~ 2 entradas de mio". Por favor, defina "mio". Además, ¿cuáles son los valores posibles de view_user.state y cuántas filas para cada valor posible? Por último, ¿para qué valores se suministran: 1 y: 2? Gracias. –

+0

sry, mio ​​es un millón. El conjunto de devolución completo de la selección interna podría ser aproximadamente el 80% de la vista completa (usuario_verdad) debido a "this.state =: 1" con el valor 2 para: 1. –

Respuesta

1

El SQL que usted proporciona no se ve como una consulta generada por Hibernate. ¿Estás seguro de que no es una consulta manuscrita?

Si desea utilizar Hibernate, puede usar setMaxResults() para limitar el número de filas devueltas.

Si desea utilizar una consulta escrita a mano que te quieren algo como esto:

select * 

a partir de ( seleccionar this.a, this.b, this.state, this.id de este view_user donde this.state =: 1 ordenar por asc this.a, this.b ), donde rownum < =: 2

+0

Sry para el error tipográfico. La consulta se ha simplificado, pero sigue siendo la misma lógicamente. La hibernación parece un poco más complicada debido a la lista de columnas en la selección. –

+0

np. ¿Hay un índice en VIEW_USER.USER_STATE_ID? –

+0

sí, hay. state_id es una clave externa a una tabla donde state_id es la clave principal. –

0

Después de una gran cantidad de depuración y tratando alrededor, tiene una respuesta de cómo solucionar el problema. Sin embargo, todavía no puedo explicar cuál es exactamente el problema.

Parece que el problema está en el nivel de agrupación de conexiones JDBC. Mi config fue:

  • INITIALSIZE = 0
  • minidle = 10

Usando la configuración anterior, tuvimos los síntomas como se describe en la Descritpion cuestión. La solución hasta ahora parece ser establecer minIdle igual a initialSize. De lo contrario, la conexión parece inicializarse de alguna manera diferente y vemos que el rendimiento disminuye. Entonces, atm lo configuramos a '10'.

Actualmente estoy analizando logs. En el servidor de base de datos, los archivos de traza no muestran ninguna diferencia. El rastreo de JDBC no mostró ninguna información interesante hasta el momento.

Google, vi que una gran cantidad de configuración de muestra establece minIdle = initialSize. Además, encontré dos citas (spring document, vmware docu) sobre la configuración de JDBC:

minIdle: El número mínimo de conexiones establecidas que deben mantenerse en la piscina en todo momento. El grupo de conexiones puede reducirse por debajo de este número si las consultas de validación fallan. El valor predeterminado se deriva de initialSize.

Tenga en cuenta que la última frase "derivada de initialSize" no se encuentra en todas las documentaciones para minIdle. La mayoría de las documentaciones mencionan '0' como predeterminado.

+0

bien, la solución es nok. Todas las conexiones que se inicializan al principio (debido a initialSize) son correctas y darán lugar a consultas rápidas, es decir, se toma el plan de consulta correcto. Pero tan pronto como se crea una nueva conexión porque no se creó lo suficiente al inicio, las conexiones creadas recientemente tendrán el problema de rendimiento. –

1

La solución final del problema fue c3p0. No pudimos rastrear por qué se eligieron los planes de consulta incorrectos. Nuestra suposición final es que tiene algo que ver con la inicialización de la conexión de dbcp. Pero debido al lago de tiempo, probamos c3p0. Las primeras experiencias:

  • problema no aparece
  • más rápido porque mucho más agresivo en cuanto al uso de maxConnections configurados para la piscina
  • y debido al punto anterior, nuestra aplicación completa es ahora más rápido. Esto se basa en la impresión general al usar la aplicación así como en nuestras diversas pruebas de carga

Por lo tanto, de momento estamos probando con entusiasmo varios escenarios con c3p0 para estar listos para los sistemas productivos.

¡Gracias por todas las entradas!

4

Me encontré con una situación similar, por lo que podría ser relevante para usted.

El controlador JDBC está cambiando los parámetros a unicode para que varchar se convierta en nvarchar cuando llegue a la base de datos. Si tiene suerte, SQL 2008 SP1 detectará esto y lo convertirá nuevamente. Pero SQL 2000 y SQL 2005 no lo harán y el optimizador de consultas hará un escaneo completo de la tabla ignorando los índices.

Es posible que pueda corregir esto en la capa JDBC agregando un parámetro de conexión sendStringParametersAsUnicode=FALSE a su cadena de conexión.

+0

Gracias esto funcionó una delicia. Estoy usando hibernate 4.2.16 y SQL Server 2008 SP4 y sqlJdbc4 controladores de Microsoft. Estaba teniendo el mismo problema en el que estaba haciendo exactamente la misma consulta en un cliente sql y volvería instantáneamente y, a través de java + hibernate, tardaría entre 4 y 5 segundos. Agregado arriba y BAM :) – kohlerfc

0

Hay una respuesta por encima/debajo de alrededor de nvarchars con la configuración de SQL Server - aquí está un ajuste similar para Oracle ...

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

Esto a veces se hace 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.

-1

Tuvimos el mismo problema: al ejecutar la consulta de SQL Developer, el plan de explicación mostró que se utilizó el índice, pero al ejecutar la consulta en la aplicación a través de Hibernate, se ignoró el índice.

Lo solucionamos agregando una sugerencia en la consulta al optimizador de Oracle para usar el índice en cuestión.

Cuestiones relacionadas