【问题标题】:Why are my NHibernate queries so slow?为什么我的 NHibernate 查询这么慢?
【发布时间】:2013-06-12 11:50:54
【问题描述】:

我正在构建一个使用 Fluent NHibernate v2.0.50727 到 SQL 服务器后端的 ASP Web 表单应用程序。

由于某些原因,查询包含小数据(约 14 行)的表需要几秒钟。

地图都很简单:Id(x => x.Id) 和 Map(x => x.Name)。 CertificateGroup 还 Map() 的 Color 和 Rank。

我已经隔离了用于测试的 nhibernate 代码:(这不代表我的应用程序,但为 SO 进行了简化和隔离)

protected void Page_Load(object sender, EventArgs e)
{
    var config = Fluently.Configure()
                             .Database(MsSqlConfiguration.MsSql2008.ConnectionString("..."))
                             .Mappings(m => m.FluentMappings.AddFromAssemblyOf<PersonMap>())                                 .BuildConfiguration();
    var factory = config.BuildSessionFactory();

    using (var session = factory.OpenSession())
    {
        using (var transaction = session.BeginTransaction())
        {
            var departments = session.QueryOver<DepartmentModel>().List(); // these all take seconds to execute - this has 14 results
            var jobs = session.QueryOver<JobModel>().List(); // 113 results
            var certificates = session.QueryOver<CertificateModel>().List(); //this one about 4 seconds for 210 results
            var groups = session.QueryOver<CertificateGroupModel>().List();

            var association = new CertificateAssociationModel
                {
                    Department = departments.First(),
                    Job = jobs.First(),
                    Certificate = certificates.First(),
                    Group = groups.First()
                };
            session.SaveOrUpdate(association);
            transaction.Commit();
        }
    }
}

我将 nhibernate 换成实体框架并运行上面的代码,并且获取是即时的。

任何帮助表示赞赏。

编辑:

以下是映射(正如我在上面解释的): 对于部门、职位和证书:

public class DepartmentMap : ClassMap<DepartmentModel>
{
    public DepartmentMap()
    {
        Table("tblDepartment");
        Id(x => x.Id);
        Map(x => x.Name);
    }
}

CertificateGroupModel也有 Map(x =&gt; x.Rank); Map(x =&gt; x.Color);

实体类都是一样的:

public class CertificateModel
{
    public virtual Int32 Id { get; protected set; }
    public virtual String Name { get; set; }
}

除了 CertificateGroupModel 之外还有:

public virtual String Color { get; set; }
public virtual Int32 Rank { get; set; }

这是我的 NHirate 分析器结果:

 -- statement #1
begin transaction with isolation level: Unspecified

-- statement #2
SELECT this_.Id   as Id4_0_,
       this_.Name as Name4_0_
FROM   tblDepartment this_

-- statement #3
SELECT this_.Id   as Id5_0_,
       this_.Type as Type5_0_
FROM   tblJobTitles this_

-- statement #4
SELECT this_.Id   as Id2_0_,
       this_.Name as Name2_0_
FROM   tblCertificate this_

-- statement #5
SELECT this_.Id    as Id1_0_,
       this_.Name  as Name1_0_,
       this_.Rank  as Rank1_0_,
       this_.Color as Color1_0_
FROM   tbl_certificate_groups this_

-- statement #6
INSERT INTO lnk_certificate_associations
            (Certificate_id,
             Department_id,
             Job_id,
             Group_id)
VALUES      (1 /* @p0 */,
             1 /* @p1 */,
             1 /* @p2 */,
             1 /* @p3 */);



select SCOPE_IDENTITY()


-- statement #7
commit transaction

nhibprofile http://i.snag.gy/bTKHm.jpg

生成的IL代码:

                      var departments = session.QueryOver<DepartmentModel>().List();
IL_008F: ldloc.2      /* session */
IL_0090: callvirt     instance NHibernate.IQueryOver`2<!!0, !!0> NHibernate.ISession::QueryOver<Core.Domain.Model.DepartmentModel>()
IL_0095: callvirt     instance [mscorlib]System.Collections.Generic.IList`1<!0> NHibernate.IQueryOver`1<Core.Domain.Model.DepartmentModel>::List()
IL_009A: stloc.s      departments

【问题讨论】:

  • 我在这里没有看到任何异常,也许除了在每个页面加载时创建ISessionFactory;它应该只做一次。除了它应该和 EF 一样好运行之外,生成的 SQL 不言自明,没有什么特别之处。
  • 不,我刚刚隔离了问题代码以进行测试/解决。创建工厂没有慢。问题在于查询。显然,在我的应用程序中,我重用了会话工厂。
  • 生成的SQL确实是正确的。我必须等待几秒钟才能执行 session.QueryOver&lt;DepartmentModel&gt;().List(); 等。我不明白为什么需要几秒钟。
  • NhibernateQueryInterceptor 是什么?
  • 它可能正在懒惰地初始化一些东西......我不知道......例如代理类生成,JIT 编译...从 NH 获取调试程序集,并在这 10 秒内暂停调试器,看看它在哪里花费时间。使用 SQL 分析器观察它何时开始执行查询。写一些日志。我不认为我们在这里看到的代码是问题所在。

标签: c# sql performance nhibernate fluent-nhibernate


【解决方案1】:

猜测一下,因为您还没有在此处发布您的映射和表格...

您的数据库中是否有任何可空列映射到不可空类型(例如,可空 int 映射到模型中的 int)?

这可能会导致您看到的那种行为,因为当 NHibernate 加载您的实体时(让我们假设 DepartmentModel 的上述场景,其属性 Prop1 是一个 int),如果 Prop1 在数据库中为空,那么会话将有一个 null 的内部状态,但是由于你不能设置 Prop1 = null,它将被分配一个值 0,当会话检查是否有任何改变时,它会看到 Prop1 已经改变(从 null 到 0),并且导致一大堆更新,这反过来会大大减慢速度。

如果是这种情况,解决方案是更新您的表定义或更新您的实体以具有正确的(可为空的)类型。

发生这种情况的方式有很多种,我只是举了一个简单的例子——如果你能包含你的映射、表定义和实体类会很有帮助...

【讨论】:

  • 我的数据库表与映射的内容匹配,但 CertificateModel 除外,它还有一些暂时未映射的列。将更新以包含实体类,但它们与您想象的完全一样。
  • 我不认为这可能是数据库问题,因为实体框架没有问题(具有实体框架等效映射)
  • Entity Framework 无法处理与 NHibernate 相同的空/脏问题 - 话虽如此,唯一可能出现此问题的属性是 CertificateGroupModel 上的 Rank,而慢查询是在您查询之前- 也许尝试使用 dotTrace 或类似的东西进行分析?
  • 我已更新问题以包含 NHibernateProfiler 图像,其中选择语句需要几秒钟。由于某种原因,DepartmentModel Name 在 db 中可以为空,CertificateGroup Rank 也是如此。它们现在不可为空,并且性能相同。
  • 查询本身不应该花费很长时间(否则使用 E​​F 会很慢),因此代码中的某些内容需要时间 - 如果您的查询是很好,因此我建议使用 .NET 分析工具来分析您的应用程序本身,这应该会告诉您导致问题的原因
【解决方案2】:

至于插入问题,如果您使用身份生成器作为 ID,nHibernate 将首先要求数据库 (INSERT...SELECT SCOPE_IDENTITY()) 为您的实体创建记录,然后使用正确的 ID 和数据 (UPDATE) 插入它

请注意,对其他实体的引用将在UPDATE 中更新,因此您只能看到“?”在插入中

【讨论】:

  • 有意思,插入后好像没有更新?
  • 那是因为不是 EdwardMulraney,@tchrikch 错了。 NHibernate 需要在插入后使用 Id 更新模型,以便选择回 Id,这可以防止批量插入发生,因此不建议您使用 Identity(它也有很多其他问题)philliphaydon.com/2011/09/…
  • @EdwardMulraney 尝试使用集合保存新实体,您会看到它。
  • @tchrikch 仅在您通过反转集合映射错误时才会出现。
猜你喜欢
  • 2011-03-07
  • 2011-03-11
  • 2017-01-21
  • 2021-03-12
  • 2020-03-19
  • 2014-03-12
  • 2011-02-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多