简单地使用NHibernate(使用代码映射)非常慢

时间:2012-07-27 01:29:47

标签: c# database performance nhibernate mapping-by-code

所以我已经没想到为什么这么慢了。也许你可以帮忙。 所以我试图通过代码使用nHibernate映射从oracle db做一个简单的get on record。我正在使用nuget的nHibernate verison 3.3.1.4。

这是映射代码:

public class Person
{
    public virtual PersonKey Key { get; set; }
    public virtual string FirstName { get; set; }
    public virtual string LastName { get; set; }
}

public class PersonKey
{
    public virtual string PersonId { get; set; }

    public override bool Equals(object obj)
    {
        if (obj == null)
            return false;
        var t = obj as PersonKey;
        if (t == null)
            return false;
        if (PersonId == t.PersonId)
            return true;
        return false;
    }
    public override int GetHashCode()
    {
        return (PersonId).GetHashCode();
    }
}

public class PersonMap : ClassMapping<Person>
{
    public PersonMap()
    {
        Schema("MyDB");
        Table("Person");
        ComponentAsId(id => id.Key, idMapper => idMapper.Property(p => p.PersonId));
        Property(i => i.FirstName);
        Property(i => i.LastName);
    }
}

以下是创建sessionfactory并检索数据的代码。

var mapper = new ModelMapper();
        var cfg = new Configuration();

        mapper.AddMappings(Assembly.GetExecutingAssembly().GetExportedTypes());

        cfg.DataBaseIntegration(c =>
        {
            c.ConnectionString = @"User Id=user;Password=password;Data Source=MyDB;";
            c.Driver<OracleClientDriver>();
            c.Dialect<Oracle10gDialect>();

            c.LogSqlInConsole = true;
            c.LogFormattedSql = true;
            c.AutoCommentSql = true;
        });

        cfg.AddMapping(mapper.CompileMappingForAllExplicitlyAddedEntities());
        var sessionFactory = cfg.BuildSessionFactory();

        stopwatch.Stop();
        Console.WriteLine("Building session factory: {0}", stopwatch.ElapsedMilliseconds);
        stopwatch.Restart();

        Person entity = null;

        using (var session = sessionFactory.OpenSession())
        using(var tx = session.BeginTransaction())
        {
            entity = (Person) session.Get("Person", new PersonKey(){PersonId = "1"});
            tx.Commit();
        }

生成的查询如下:

SELECT person0_.PersonId as PersonId0_0_, 
person0_.FirstName as FirstName0_0_, person0_.LastName as LastName0_0_,  
FROM MyDB.Person person0_
WHERE person0_.PersonId=:p0;
:p0 = '1' 

平均需要大约80-100秒才能访问检索到的对象。这包括创建会话。

所以我寻找了一些显而易见的事情:

  • personId coloumn已编入索引(它是表格的主键)。
  • 数据库在服务器上,所以为了检查它不是网络占用时间,我使用AD0.Net(Oracleconnection + Oraclecommand)运行上面生成的查询。这大约需要180ms,包括创建连接并将记录映射到实体类。
  • 通过PL / SQL开发人员调查查询(大约需要32ms)。
  • 检查生成的查询的查询计划(确认使用索引唯一扫描而不是全表扫描)。
  • 我在我的本地运行上面的代码对类似大小的sql 2012 db,并且通过NHibernate代码大约180ms,这是疯狂的快速比较。
  • Ran nhprof(评估)并得到以下结果:

Results from NHibernate profiler

似乎运行查询并且结果从数据库中快速返回(根据nhprof结果),但可能是查询值的水合作用占用了占用时间的实体。我不知道什么可以扼杀时间!

我的下一步是将nHibernate源附加到解决方案并逐步完成,但工作中的下载访问权限有限(甚至到github!)。那么任何想法呢?

干杯。

更新:所以我在我的项目中获得了nHibernate源代码并逐步完成了它。一切都在顺利进行,直到程序得到这段代码:

for (count = 0; count < maxRows && rs.Read(); count++)              

rs.Read()上,执行会占用时间,其中rs是oracle datareader。这段代码位于Loader.cs文件中的DoQuery函数中。

奇怪的是,如果传入的查询是非参数化的动态查询(例如select ... from person where personid = '1'),则执行快速点亮(~1ms),但如果其参数化(例如select ... from person where personid = :p1)那么它的速度非常慢。由于我想利用nHibernate的强大功能,我需要使用生成的参数化查询。我仍在试图找出oracle读者的原因

2 个答案:

答案 0 :(得分:1)

发现问题和解决方案由Deep Shah撰写here

基本上,NHibernate中的参数化查询与oracle的microsoft驱动程序是造成瓶颈的原因。

他继续分享两个解决方案:

  1. 使用oracle开发的Oracle数据客户端驱动程序替换当前的Microsoft oracle驱动程序。使用新驱动程序运行相同的查询消除了性能bottelneck。为此,您需要在要从中触发查询的计算机上安装Oracle数据访问组件(ODAC)。

  2. 当通过NHibernate在查询上设置参数时,在查询VARCHAR列时使用“query.SetAnsiString”而不是“query.SetParameter”。

  3. 我已经验证了这两种解决方案,但它们都有效。解决方案一是我使用的那个,因为我使用的是代码映射,我将查询生成留给nHibernate。

答案 1 :(得分:0)

我相信你也可以在你的映射中将数据类型设置为ansi字符串,如本文所述: NHibernate 3 specify sql data type with loquacious syntax