我有一个使用NHibernate作为其ORM的应用程序,有时它会遇到性能问题,因为它是如何访问数据的.可以做些什么来改善NHibernate的性能?(每个答案限于一个建议)
NHibernate可能遇到的第一个也是最引人注目的性能问题是,如果要为每个创建的会话创建一个新的会话工厂.应为每个应用程序执行只创建一个会话工厂实例,并且该工厂应创建所有会话.
除此之外,只要有意义,就应该继续使用相同的会话.这将因应用程序而异,但对于大多数Web应用程序,建议每个请求使用一个会话.如果您经常丢弃会话,则无法获得缓存的好处.智能地使用会话高速缓存可以将具有线性(或更差)查询数量的例程更改为常数,而无需太多工作.
同样重要的是,您要确保延迟加载对象引用.如果不是这样,即使是最简单的查询,也可以加载整个对象图.只有某些原因不能这样做,但最好从延迟加载开始并根据需要切换回来.
这让我们渴望获取,与延迟加载相反.在遍历对象层次结构或循环遍历集合时,很容易丢失您正在进行的查询数量,并最终得到指数级的查询.可以使用FETCH JOIN在每个查询的基础上进行急切提取.在极少数情况下,例如,如果有一对特定的表总是获取连接,请考虑关闭该关系的延迟加载.
与往常一样,SQL事件探查器是查找运行缓慢或重复运行的查询的好方法.在我上一份工作中,我们有一个开发功能,它还计算每页请求的查询次数.对例程的大量查询是最明显的指标,表明您的例程与NHibernate不兼容.如果每个例程或请求的查询数看起来不错,那么您可能需要进行数据库调优; 确保您有足够的内存来存储缓存中的执行计划和数据,正确索引数据等.
我们遇到的一个棘手的问题是SetParameterList().该功能允许您轻松地将参数列表传递给查询.NHibernate通过为传入的每个项创建一个参数来实现这一点.这会为每个参数数量生成不同的查询计划.我们的执行计划几乎总是从缓存中释放出来.此外,许多参数可能会显着减慢查询速度.我们做了NHibernate的自定义hack,将项目作为分隔列表发送到单个参数中.该列表在SQL Server中由表值函数分隔,我们的hack自动插入到查询的IN子句中.根据您的应用,可能会有其他类似的地雷.SQL事件探查器是找到它们的最佳方式.
NHibernate的SessionFactory是一个昂贵的操作,所以一个好的策略是创建一个Singleton,它确保内存中只有一个SessionFactory实例:
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(); } }
然后在您的Global.Asax Application_Startup中,您可以初始化它:
protected void Application_Start() { NHibernateSessionManager.Instance.Initialize(); }
通过识别何时从延迟加载切换到针对执行缓慢的查询的急切提取来避免和/或最小化选择N + 1问题.
没有推荐,只是一个帮助你的工具:NH Prof(http://nhprof.com/)似乎很有前途,它可以评估你对ORM框架的使用.它可以是您调整NHibernate的一个很好的起点.