NHibernate如何提高性能?

61

我有一个应用程序,它使用NHibernate作为其ORM,有时由于数据被访问的方式而出现性能问题。 有哪些方法可以提高NHibernate的性能?(每个答案请限制为一条建议)

我建议你考虑使用二级缓存来优化NHibernate的性能。二级缓存可用于缓存经常访问的数据,从而减少数据库查询的数量并提高应用程序的响应速度。要使用二级缓存,请配置合适的缓存提供程序,并在需要缓存的实体类上添加适当的缓存设置。

12个回答

54
NHibernate可能会遇到的第一个和最严重的性能问题是,如果您为每个session创建一个新的session工厂,则只应为每个应用程序执行创建一个session工厂实例,并通过该工厂创建所有的sessions。
沿着这些线路,您应该在使用相同的session,只要它有意义。这将因应用程序而异,但对于大多数Web应用程序,每个请求一个单独的session是建议的。 如果您经常丢弃会话,则无法获得其缓存的好处。智能地使用session缓存可以在不多做工作的情况下将具有线性(或更差)查询数量的过程更改为具有恒定数量的过程。
同样重要的是,您要确保延迟加载对象引用。如果没有,即使是最简单的查询也可能会加载整个对象图。虽然只有某些原因不这样做,但始终从延迟加载开始并根据需要切换回来总是更好的。
这就带我们来到急切获取,与延迟加载相反。当遍历对象层次结构或循环遍历集合时,很容易失去追踪您正在进行多少次查询,并且最终会出现指数级查询的情况。可以通过FETCH JOIN对每个查询进行急切获取。在极少数情况下,例如如果有一对特定的表始终进行联接,则考虑关闭该关系的延迟加载。
像往常一样,SQL Profiler是查找运行缓慢或重复进行的查询的好方法。在我的上一份工作中,我们还拥有一个开发功能,用于计算每个页面请求的查询数。每个过程的查询数较高是指示您的NHibernate无法很好地运行的最明显的指标。如果每个过程或请求的查询数看起来不错,那么您可能将其降至数据库调整; 确保您具有足够的内存以存储执行计划和缓存中的数据,正确索引您的数据等。

我们遇到的一个棘手问题是 SetParameterList()。该函数允许您轻松地将参数列表传递给查询。NHibernate通过为每个传入的项创建一个参数来实现此功能。这导致每个参数数量都有不同的查询计划。我们的执行计划几乎总是从缓存中释放。此外,大量的参数会严重拖慢查询速度。我们对 NHibernate 进行了自定义修改,将项目作为分隔符列表发送到单个参数中。在 SQL Server 中,该列表由我们的修改自动插入到查询的 IN 子句中的表值函数中进行分离。根据您的应用程序可能还存在其他类似的问题。使用 SQL Profiler 可以找到它们。


1
NH默认不会使用延迟加载对象引用吗?如果不是,你要怎么强制实现呢? - sydneyos
如果您正在使用Fluent NHibernate,您可以将DefaultLazy.Always()作为约定添加到您的Fluent NHibernate配置中。我不确定它在.hbm映射中如何工作,但我想您可以在NH配置中很容易地添加它。 - Anshul

26

NHibernate的SessionFactory是一个耗费昂贵的操作,因此一个好的策略是创建一个单例模式,确保内存中只有一个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();
}

以下是关于编程的内容,请将其从英语翻译成中文。请只返回翻译后的文本,不要进行解释。如果您能提供一个简单的工作示例并加以解释,我会给您点赞。如果可以的话,我希望有一个功能能够为好的答案提供多个点赞! - Yordan Georgiev

11

不是一则建议,而是一项帮助工具:NH Prof(http://nhprof.com/)似乎很有前途,它可以评估您对ORM框架的使用。这可能是您调整NHibernate的一个良好起点。


1
没错。先让它能用,然后再让它跑得快。像 NH Prof 这样的分析工具可以显示瓶颈。 - Matt Hinze

11

当查询的性能较慢时,通过识别何时从延迟加载切换到急切获取来避免和/或最小化选择N + 1问题


不同意解决方案。保持懒加载,但启用批处理获取 懒加载。这可以消除N+1问题,并对代码影响最小。我在我的另一个答案这里中有更多相关内容。 - Frédéric

4

没有关于你所遇到的性能问题的具体信息,我只能提供一个概括:在我的经验中,大多数数据库查询性能问题都源于缺乏适当的索引。因此,我的建议是首先检查未建立索引的查询计划。


3
NHibernate开箱即用生成非常快的SQL。 我已经使用了一年,但从未不得不使用裸SQL。 我所有的性能问题都来自正规化和缺乏索引。
最简单的解决方法是检查查询的执行计划并创建适当的索引,特别是在外键列上。如果您正在使用Microsoft SQL Server,则“数据库引擎调整顾问”可以很好地帮助解决这个问题。

3
每个答案只有一个建议?那么我会选择这个:
由于沿着两个或多个并行的to-many关联进行连接,避免连接重复项(又称笛卡尔积);改用Exists-subqueries、MultiQueries或FetchMode“subselect”。
引自:Hibernate性能调整技巧

1

如果你还没有恰当地使用延迟加载,请开始使用。在不需要时获取集合是一种浪费。

章节提高性能描述了这种以及其他提高性能的方法。


1

性能分析是第一步 - 即使是简单的定时单元测试 - 以找出可以获得最大收益的地方

对于集合,请考虑设置批处理大小以减少发出的选择语句数量 - 有关详细信息,请参见提高性能部分


1

我只允许限制我的答案为一个选项?在这种情况下,我会选择实现NHibernate的二级缓存机制。

这样,对于映射文件中的每个对象,您都可以定义缓存策略。第二级缓存将在内存中保留已检索的对象,因此不会再次返回数据库。这是一个巨大的性能提升。

您的目标是定义应用程序不断访问的对象。其中包括一般设置等。

有很多关于nhibernate第二级缓存以及如何实现它的信息。

祝你好运 :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接