Tengo una aplicación que usa NHibernate como ORM y, a veces, experimenta problemas de rendimiento debido a cómo accede a los datos. ¿Qué tipo de cosas se pueden hacer para mejorar el rendimiento de NHibernate? (Limite a una recomendación por respuesta)
El primer y más dramático problema de rendimiento con el que se puede encontrar con NHibernate es si está creando una nueva fábrica de sesiones para cada sesión que cree. Solo se debe crear una instancia de fábrica de sesión para cada ejecución de la aplicación y todas las sesiones deben ser creadas por esa fábrica.
En ese sentido, debe continuar usando la misma sesión siempre que tenga sentido. Esto variará según la aplicación, pero para la mayoría de las aplicaciones web, se recomienda una sola sesión por solicitud. Si descarta su sesión con frecuencia, no está obteniendo los beneficios de su caché. El uso inteligente de la caché de sesión puede cambiar una rutina con un número lineal (o peor) de consultas a un número constante sin mucho trabajo.
Igualmente importante es que desea asegurarse de que es perezoso al cargar sus referencias de objeto. Si no lo está, se podrían cargar gráficos de objetos completos incluso para las consultas más simples. Solo hay ciertas razones para no hacer esto, pero siempre es mejor comenzar con una carga lenta y volver a cambiar según sea necesario.
Eso nos lleva a buscar ansioso, lo contrario de la carga perezosa. Mientras recorre las jerarquías de objetos o recorre las colecciones, puede ser fácil perder la noción de cuántas consultas está haciendo y termina con un número exponencial de consultas. La búsqueda ansiosa se puede realizar por consulta con FETCH JOIN. En circunstancias excepcionales, como si hay un par particular de tablas a las que siempre se une, considere desactivar la carga diferida para esa relación.
Como siempre, SQL Profiler es una excelente manera de encontrar consultas que se ejecutan lentamente o que se realizan repetidamente. En mi último trabajo teníamos una función de desarrollo que también contaba las consultas por solicitud de página. Una gran cantidad de consultas para una rutina es el indicador más obvio de que su rutina no funciona bien con NHibernate. Si el número de consultas por rutina o solicitud se ve bien, es probable que tenga que ajustar la base de datos; asegurándose de tener suficiente memoria para almacenar planes de ejecución y datos en la memoria caché, indexar correctamente sus datos, etc.
Un pequeño problema complicado con el que nos encontramos fue con SetParameterList (). La función le permite pasar fácilmente una lista de parámetros a una consulta. NHibernate implementó esto creando un parámetro para cada elemento pasado. Esto da como resultado un plan de consulta diferente para cada número de parámetros. Nuestros planes de ejecución casi siempre se liberaban del caché. Además, numerosos parámetros pueden ralentizar significativamente una consulta. Hicimos un truco personalizado de NHibernate para enviar los elementos como una lista delimitada en un solo parámetro. La lista estaba separada en SQL Server por una función de valor de tabla que nuestro hack insertó automáticamente en la cláusula IN de la consulta. Podría haber otras minas terrestres como esta dependiendo de su aplicación. SQL Profiler es la mejor manera de encontrarlos.
SessionFactory de NHibernate es una operación costosa, por lo que una buena estrategia es crear un Singleton que garantice que solo haya UNA instancia de SessionFactory en la memoria:
public class NHibernateSessionManager
{
private readonly ISessionFactory _sessionFactory;
public static readonly NHibernateSessionManager Instance = new NHibernateSessionManager();
private NHibernateSessionManager()
{
if (_sessionFactory == null)
{
System.Diagnostics.Debug.WriteLine("Factory was null - creating one");
_sessionFactory = (new Configuration().Configure().BuildSessionFactory());
}
}
public ISession GetSession()
{
return _sessionFactory.OpenSession();
}
public void Initialize()
{
ISession disposeMe = Instance.GetSession();
}
}
Luego, en su Global.Asax Application_Startup, puede inicializarlo:
protected void Application_Start()
{
NHibernateSessionManager.Instance.Initialize();
}
Evite y/o minimice el Seleccione el problema N + 1 al reconocer cuándo cambiar de carga lenta a búsqueda ansiosa para consultas de rendimiento lento.
No es una recomendación sino una herramienta para ayudarlo: NH Prof ( http://nhprof.com/ ) parece ser prometedor, puede evaluar su uso del marco ORM. Puede ser un buen punto de partida para su ajuste de NHibernate.
Sin ningún detalle sobre los tipos de problemas de rendimiento que está viendo, solo puedo ofrecer una generalización: en mi experiencia, la mayoría de los problemas de rendimiento de las consultas de bases de datos surgen de la falta de índices adecuados. Entonces, mi sugerencia para una primera acción sería verificar sus planes de consulta para consultas no indexadas.
¿"Una recomendación por respuesta" solamente? Entonces iría por este:
Evite los duplicados de unión (también conocidos como productos cartesianos) debido a las uniones a lo largo de dos o más asociaciones paralelas a muchas; use Exists-subqueries, MultiQueries o FetchMode "subselect" en su lugar.
Tomado de: Hibernate Performance Tuning Tips
NHibernate genera SQL bastante rápido desde el primer momento. Lo he estado usando durante un año y aún no he tenido que escribir SQL simple con él. Todos mis problemas de rendimiento han sido de Normalización y falta de índices.
La solución más fácil es examinar los planes de ejecución de sus consultas y crear índices adecuados, especialmente en sus columnas de clave externa. Si está utilizando Microsoft SQL Server, el "Asesor de ajuste de motor de base de datos" ayuda mucho con esto.
¿Solo puedo limitar mi respuesta a una opción? En ese caso, seleccionaría que implemente el mecanismo de caché de segundo nivel de NHibernate.
De esta manera, para cada objeto en su archivo de mapeo puede definir la estrategia de caché. El caché de segundo nivel mantendrá los objetos ya recuperados en la memoria y, por lo tanto, no realizará otro viaje de ida y vuelta a la base de datos. Este es un gran refuerzo de rendimiento.
Su objetivo es definir los objetos a los que accede constantemente su aplicación. Entre ellos habrá ajustes generales y similares.
Hay mucha información para encontrar la caché de segundo nivel nhibernate y cómo implementarla.
Buena suerte :)
La creación de perfiles es el primer paso, incluso pruebas unitarias cronometradas simples, para descubrir dónde se pueden obtener las mayores ganancias
Para las colecciones, considere configurar el tamaño del lote para reducir el número de declaraciones seleccionadas emitidas; consulte la sección Mejora del rendimiento para obtener más detalles
Si aún no está utilizando la carga diferida (adecuadamente), comience. Obtener colecciones cuando no las necesita es un desperdicio de todo.
Capítulo Mejora del rendimiento describe esta y otras formas de mejorar el rendimiento.
Almacenamiento en caché, almacenamiento en caché, almacenamiento en caché: ¿está utilizando correctamente el almacenamiento en caché de primer nivel [cierre de sesiones prematuramente o está utilizando StatelessSession para omitir el almacenamiento en caché de primer nivel]? ¿Necesita configurar una caché de segundo nivel simple para valores que cambian con poca frecuencia? ¿Puede almacenar en caché los conjuntos de resultados de consultas para acelerar las consultas que cambian con poca frecuencia?
[También configuración: ¿puede establecer elementos como inmutables? ¿Puede reestructurar las consultas para recuperar solo la información que necesita y transformarlas en la entidad original? ¿Podrá Batman detener al Acertijo antes de que llegue a la presa? ... oh, lo siento, me dejé llevar.]
Lo que dijo muchísimo tiempo libre.
Lea el Capítulo 19 de la documentación, "Mejora del rendimiento".
NHibernate: http://nhibernate.info/doc/nhibernate-reference/performance.html
Hibernate: http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html
Use el Analizador de SQL (o equivalente para la base de datos que está usando) para localizar consultas de larga duración. Optimice esas consultas con los índices adecuados.
Para las llamadas a la base de datos que se utilizan en casi todas las páginas de una aplicación, use CreateMultiQuery para devolver múltiples conjuntos de resultados de una sola consulta a la base de datos.
Y por supuesto, caché. La directiva OutputCache para páginas/controles. Almacenamiento en caché de NHibernate para datos.