提高NHibernate性能的最佳方法是什么?

时间:2008-09-15 21:22:53

标签: .net performance nhibernate orm data-access-layer

我有一个使用NHibernate作为其ORM的应用程序,有时它会因为数据访问方式而遇到性能问题。可以做些什么来改善NHibernate的性能? (每个答案限于一个建议)

12 个答案:

答案 0 :(得分:53)

您可以使用NHibernate遇到的第一个也是最引人注目的性能问题是,如果要为您创建的每个会话创建一个新的会话工厂。每个应用程序执行只应创建一个会话工厂实例,并且该工厂应创建所有会话。

除此之外,只要有意义,就应该继续使用相同的会话。这将因应用程序而异,但对于大多数Web应用程序,建议每个请求使用一个会话。如果您经常丢弃会话,则无法获得缓存的好处。智能地使用会话高速缓存可以将具有线性(或更差)查询数量的例程更改为常数,而无需太多工作。

同样重要的是,您要确保延迟加载对象引用。如果不是这样,即使是最简单的查询,也可以加载整个对象图。只有某些原因不能这样做,但最好从延迟加载开始并根据需要切换回来。

这让我们急切地想要,与延迟加载相反。在遍历对象层次结构或循环遍历集合时,很容易丢失您正在进行的查询数量,并最终得到指数级的查询。可以使用FETCH JOIN在每个查询的基础上进行急切提取。在极少数情况下,例如,如果有一对特定的表总是获取连接,请考虑关闭该关系的延迟加载。

与往常一样,SQL事件探查器是查找运行缓慢或重复运行的查询的好方法。在我上一份工作中,我们有一个开发功能,它还计算每页请求的查询次数。对例程的大量查询是最明显的指标,表明您的例程与NHibernate不兼容。如果每个例程或请求的查询数看起来不错,那么您可能需要进行数据库调优;确保您有足够的内存来存储缓存中的执行计划和数据,正确索引数据等等。

我们遇到的一个棘手的问题是SetParameterList()。该功能允许您轻松地将参数列表传递给查询。 NHibernate通过为传入的每个项创建一个参数来实现这一点。这会为每个参数数量生成不同的查询计划。我们的执行计划几乎总是从缓存中释放出来。此外,许多参数可能会显着减慢查询速度。我们做了NHibernate的自定义hack,将项目作为分隔列表发送到单个参数中。该列表在SQL Server中由表值函数分隔,该值函数将我们的hack自动插入到查询的IN子句中。根据您的应用,可能会有其他类似的地雷。 SQL事件探查器是找到它们的最佳方式。

答案 1 :(得分:26)

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();
}

答案 2 :(得分:11)

通过识别何时从延迟加载切换到针对执行缓慢的查询的急切提取来避免和/或最小化Select N + 1 problem

答案 3 :(得分:10)

没有建议,只有一个工具来帮助你:NH教授(http://nhprof.com/)似乎很有希望,它可以评估你对ORM框架的使用。这可能是您调整NHibernate的一个很好的起点。

答案 4 :(得分:4)

如果没有任何关于您所看到的性能问题的具体细节,我只能提供一个概括:根据我的经验,大多数数据库查询性能问题都源于缺乏正确的索引。因此,我对第一个操作的建议是检查非索引查询的查询计划。

答案 5 :(得分:3)

NHibernate开箱即用生成相当快的SQL。 我已经使用它一年了,还没有用它写裸SQL。 我的所有性能问题都来自Normalization并且缺少索引。

最简单的解决方法是检查查询的执行计划并创建正确的索引,尤其是在外键列上。如果您使用的是Microsoft SQL Server,那么“数据库引擎优化顾问”可以为您提供很多帮助。

答案 6 :(得分:3)

“每个答案一个建议”只?然后我会去找这个:

避免加入重复项(AKA笛卡儿产品),因为两个或多个并行的多个关联的连接;使用Exists-subqueries,MultiQueries或FetchMode“subselect”代替。

取自:Hibernate Performance Tuning Tips

答案 7 :(得分:1)

如果您尚未使用延迟加载(正确),请启动。在不需要它们的情况下获取集合是浪费一切。

Chapter Improving performance介绍了提高效果的方法和其他方法。

答案 8 :(得分:1)

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

对于集合,请考虑设置批量大小以减少已发布的select语句的数量 - 有关详细信息,请参阅Improving performance部分

答案 9 :(得分:1)

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

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

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

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

祝你好运:)

答案 10 :(得分:1)

缓存,缓存,缓存 - 您是否正确使用第一级缓存[过早关闭会话,或使用StatelessSession绕过第一级缓存]?您是否需要为不经常更改的值设置简单的二级缓存?您是否可以缓存查询结果集以加速不经常更改的查询?

[还有配置 - 你可以将项目设置为不可变的吗?您是否可以重新构建查询以仅返回所需的信息并将其转换为原始实体?蝙蝠侠能够在他到达大坝之前阻止谜语吗? ......哦,抱歉被带走了。]

答案 11 :(得分:0)

很多时候都说过。

阅读文档第19章“提高绩效” NHibernate:http://nhibernate.info/doc/nhibernate-reference/performance.html
休眠:http://docs.jboss.org/hibernate/core/3.3/reference/en/html/performance.html

使用SQL事件探查器(或您正在使用的数据库的等效项)来查找长时间运行的查询。使用适当的索引优化这些查询。

对于几乎在应用程序的每个页面上使用的数据库调用,使用CreateMultiQuery从单个数据库查询返回多个结果集。

当然,缓存。页面/控件的OutputCache指令。 NHibernate缓存数据。