6

Estoy luchando por mejorar una consulta n + 1 en un proyecto en el que estoy trabajando. Utilizo Hibernate con el modelo que se muestra a continuación, y deseo expresar una consulta para recuperar todos los artículos relacionados con una cartera, incluidos los dos últimos precios de cada artículo (precio en la fecha determinada y precio anterior).Luchando para optimizar la consulta N + 1 en Hibernate

enter image description here

API de ejemplo:

List<Items> items = findItemsWithLatestTwoPrices(portfolio, latestPriceDate); 

Actualmente utilizo una consulta para extraer todos los artículos relacionados con la cartera, y luego iterar sobre los elementos para consultar los dos últimos precios de un determinado artículo (entonces n + 1).

Intenté expresar esto en sql nativo usando una subconsulta correlacionada, pero el rendimiento fue terrible. Esto y el hecho de que hay nuevos precios todos los días (por lo que la consulta es cada vez más lenta) me ha llevado a pensar que necesito un modelo diferente, pero estoy luchando por encontrar un modelo que sea razonablemente efectivo y constante a lo largo del tiempo. el número de precios aumenta

He estado pensando en diferentes soluciones, incluyendo la representación de precios como listas vinculadas, o el uso de algún tipo de árbol, pero creo que hay mejores alternativas. ¿Me estoy perdiendo algo obvio? ¿Alguien que está trabajando en un problema similar ha tenido una buena solución?

Realmente no me importa si uso HQL o SQL nativo siempre que el rendimiento sea aceptable. También estoy abierto para hacer cambios al modelo.

Gracias!

[Editar]

Ya que tengo más de dos años de datos sobre los precios, y no puede haber más de 1000 artículos pr. cartera, recuperar el gráfico completo probablemente no sea una buena idea. También necesito acceso aleatorio por fecha, por lo que el almacenamiento de los dos precios como campos en el elemento lamentablemente no es una opción.

Respuesta

0

No estoy seguro de captar todas sus preocupaciones, pero como probablemente haya pensado, no hay una solución fácil para esto con Hibernate. Se reducirá a su modelado del dominio. Creo que es mejor que separe el caso normal y el caso especial. Puede modelarlos en su dominio normal o usar representaciones especiales para casos especiales.

Para buscar los n últimos premios ¿ha intentado establecer el tamaño del lote en la relación? Haga la relación ordenada (última en la parte superior), y luego ajuste el tamaño del lote a algo así como 10. Eso haría que Hibernate consulte 10 y 10 filas, y con índices en la clave externa y la columna de orden debería funcionar bien en la mayoría casos.

También me parece que podría mantener relaciones adicionales, así como todo el conjunto. No tema mezclar explícitamente relaciones importantes como "precios de los últimos meses", aunque sería una duplicación de datos. Debería ser posible evitar la duplicación en el DB en la mayoría de los casos.

Para su acceso aleatorio en función de las fechas parece que es mejor atenderlo con una consulta personalizada en lugar de acceso a través del modelo de dominio, si son demasiado lentos considere usar el segundo nivel de almacenamiento en caché, pero supongo que su el patrón de acceso no se beneficiará mucho de esto.

0

Debería intentar recuperar los artículos Y los precios en una consulta. Si lo hace, puede iterar sobre sus artículos y sus precios sin necesidad de hacer una selección para cada artículo. Su problema n + 1 debería desaparecer.

Por ejemplo, puede utilizar la búsqueda con ganas dentro de su consulta o en la definición de su asociación.

En relación con su rendimiento, la preocupación de aumentar los objetos de precio. Tal vez pueda almacenar los precios de los dos últimos en uno o dos campos adicionales de su clase de artículo. Entonces, siempre puedes buscar esos campos extra y obtener los precios más viejos en tu colección si es necesario.

+0

Hola, gracias por responder. Tengo algunas reservas en contra de obtener todo el gráfico. Como hay precios todos los días, y podría haber hasta 1000+ artículos de cartera, eso significaría recuperar e instanciar 73000 objetos si tiene dos años de precios (como los que tenemos). He editado mi pregunta para incluir los números. En cuanto a usar un campo para los dos precios, eso solo es útil si siempre usas un precio diario, pero necesito acceso aleatorio por fecha. – ebaxt

+0

Tal vez primero pueda cargar todos los artículos de un portafolio y luego cargar todos los precios con la fecha que necesite (en una segunda consulta independiente). Después de eso, puede hacer coincidir los artículos con los precios en la memoria. Al hacer esto, puedes evitar la carga n + 1. Además de eso, no veo otra opción a excepción de impaciente cargando todo el gráfico. – GeorgeG

0

Usted puede probar un par de opciones

  1. Debido a que sus precios son basado en una fecha se puede ver en la partición de los datos en la base de datos por mes. Esto ayudará considerablemente a sus consultas ya que el número de registros para la búsqueda de precios disminuiría considerablemente en lugar de mirar los precios completos de 2 años. Pruebe la consulta SQL después de esto. También ejecute la explicación para asegurarse de que está accediendo a los índices correctos, etc.
  2. ¿Ha considerado el almacenamiento en caché (p. Ej .: Memcache)? Puede precargar los precios de sus artículos para el precio actual de caché &. A continuación, puede buscar la cartera de elementos & caché de búsqueda para los precios que debe ser bastante rápido.
0

Si está utilizando Postgre u Oracle, puede usar fácilmente un analytic/windowing function en esos precios cuando se une a ellos, recuperando los dos primeros valores. Siempre que la columna para ORDER BY esté indexada, debería proporcionar un rendimiento lo suficientemente bueno.

P.S. La próxima vez, si dice que está considerando usar SQL nativo, agregue el proveedor/versión de DB.

Cuestiones relacionadas