NHibernate + SQLite,对象图的加载时间过长

时间:2013-07-26 15:37:37

标签: c# .net database sqlite nhibernate

在程序启动时,我正在将整个数据库的完整对象图加载到域对象中,而不是延迟加载。我知道这不是典型的用法,但这不在问题之内。该数据库仅由单个用户/程序访问。以下条件是关于这个单启动加载操作;

随着数据库的增长,加载时间变得越来越长。我有这些数字用于同一数据库的两个状态;

1. Size 1.6MB, 3 main tables row count; 1100, 2400, 13400. Load time;    44s
2. Size 3.6MB, 3 main tables row count; 2800, 6200, 26700. Load time; 3m 40s

这3个表有这些列;

7x integer, 4x numeric, 6x text, 2x datetime  (2800 rows)
7x integer, 3x numeric, 11x text, 2x datetime (6200 rows)
4x integer, 2x numeric, 5x text, 1x datetime  (26700 rows)

所有映射均为1:m

CPU Profiler显示了这一点; http://img585.imageshack.us/img585/4444/6rh.png

欣赏建议;

  1. 这种情况可以(非常粗略地)预期/目标的加载时间是多少?这些时间是合理的还是完全脱离图表?
  2. 可能是什么瓶颈?
  3. 可能不同的数据库表现更好吗? (哪个?)
  4. 任何改善加载时间的相关建议

  5. 编辑:

    映射;

       public class AccountBaseMap : ClassMap<AccountBase>
        {
           public AccountBaseMap()
               : base()
           {
               Id(x => x.Id).GeneratedBy.Identity();      
               HasMany(x => x.Executions).KeyColumn("Account__Id").Cascade.All();
               HasMany(x => x.Orders).KeyColumn("Account__Id").Cascade.All();
               //...value types omitted                 
               References(x => x.RiskProfile).Cascade.All();                
           }
       }
    
       public class LocalOrderMap : ClassMap<LocalOrder>
        {
           public LocalOrderMap()
                : base()
            {
                Id(x => x.Id).GeneratedBy.Identity();
                Map(x => x.Account__Id);    
                //...value types omitted       
                HasMany(x => x.StatusDetails).KeyColumn("Order__id").Cascade.All(); 
            }
      }
    
        public class OrderStatusDetailMap : ClassMap<OrderStatusDetail>
        {
           public OrderStatusDetailMap()
                : base()
            {
                Id(x => x.Id).GeneratedBy.Identity();
                //...value types omitted         
                Map(x => x.Order__Id); 
                Map(x => x.Time).CustomType("timestamp");                 
            }
         }
    

    NHibernate ShowSql,来自查询;

      

    _session.CreateCriteria(T)()列表(T)();

    输出; (约有1000行,类似于......删除)

    http://textuploader.com/?p=6&id=ie7mn


    编辑2:

    NHibernate配置;

     var fcfg = Fluently.Configure()
         .Database(SQLiteConfiguration.Standard.ConnectionString(connString)); 
    
     fcfg.Mappings(m => m.FluentMappings.AddFromAssemblyOf<TMap1>().Conventions.Add(FluentNHibernate.Conventions.Helpers.DefaultLazy.Never()));
    
    if (typeof(TMap1) != typeof(TMap2))
        fcfg.Mappings(m => m.FluentMappings.AddFromAssemblyOf<TMap2>().Conventions.Add(FluentNHibernate.Conventions.Helpers.DefaultLazy.Never()));
    
    fcfg.ExposeConfiguration(c => cfg = c).Cache(c => c.UseQueryCache());
    
    sessionFactory = fcfg.BuildSessionFactory();
    

    我不使用任何日志记录框架,因此请不要记录日志。虽然我目前运行的是Debug版本,但应检查Release版本是否有所不同。

    关于建筑;解决方案设置为Any CPU,项目构建到x86(平台目标)。我在x86 XP和x64 Windows 7上运行,并基于此加载正确的System.Data.SQLite.dll。

    是的,我正在使用NHibernate 3.1.0.4

3 个答案:

答案 0 :(得分:0)

很少有建议/问题

  • 尝试关闭所有日志记录。
  • 为SQLite使用正确的x86 / x64 arch?
  • NH Config是什么样的?
  • 确保您使用的是正确的SQLite NH驱动程序。
  • 您是否设置了 default_schema 属性?
  • 您是否为每个请求创建 SessionFactory

如果一切都失败了:

  • 尝试SQL CE 4

在NHibernate中记录所有内容(尤其是3.x或更高版本)可能会在调试时导致重大性能问题。

答案 1 :(得分:0)

因为你在另一侧有反向引用设置反向

// in Accountmap
HasMany(x => x.Executions).KeyColumn("Account__Id").Cascade.All().Inverse();
HasMany(x => x.Orders).KeyColumn("Account__Id").Cascade.All().Inverse();

// in Accountmap
HasMany(x => x.StatusDetails).KeyColumn("Order__id").Cascade.All().Inverse();

反向引用应该是引用而不是属性

Map(x => x.Account__Id); -> References(x => x.Account);
Map(x => x.Order__Id);   -> References(x => x.Order);

在查询时使用期货批量查询以便在一次往返中获取数据

// ignore the results, the query only loads the data into session cache
session.QueryOver<Order>()
    .Fetch(x => x.StatusDetails).Eager
    .Future();

var accounts = session.QueryOver<AccountBase>()
    .Fetch(x => x.Orders).Eager
    .Future();


return accounts.ToList();    // both queries execute here

检查你的equals和gethashcode实现,以便将id考虑在内以防止核心器例中的错误

public override bool Equals(object obj)
{
    var other = obj as AccountBase;
    return other != null && IsTransient ? ReferenceEquals(this, other) : Id == other.Id;
}

private bool IsTransient { get { return Id == 0; } }

private int? _cachedHashcode;
public override int GetHashcode()
{
    if (!_cachedHashcode.HasValue)
        _cachedHashcode = IsTransient ? base.GetHashCode() : Id.GetHashCode();
    return _cachedHashcode.Value;
}

还有一些小调整:

  • 使用流利的api并删除unnessary anon delegates
  • 将Defaultlazy.Never()延迟到实际用例

    var fcfg = Fluently.Configure()
        .Database(SQLiteConfiguration.Standard.ConnectionString(connString))
        .Mappings(m => m.FluentMappings.AddFromAssemblyOf<TMap1>())
        .Cache(c => c.UseQueryCache());
    
    if (typeof(TMap1) != typeof(TMap2))
        fcfg.Mappings(m => m.FluentMappings.AddFromAssemblyOf<TMap2>()));
    
    cfg = fcfg.BuildConfiguration();
    sessionFactory = cfg.BuildSessionFactory();
    

答案 2 :(得分:0)

终于发现我只需改变一件事;

在我的班级地图中,添加到所有1:M集合Fetch.Subselect()

  

HasMany(x =&gt; x.Orders)。 Fetch.Subselect() .KeyColumn(&#34; Account _Id&#34;)。Not.LazyLoad()。Cascade.All() .Inverse();

(我还在那里添加了Not.LazyLoad(),但没有区别,因此可以省略)

通过此更改,我可以保持流畅的配置约定DefaultLazy.Never,这很好,因为我不需要将属性和方法设置为虚拟。我可以保持单向关系。基本上,除了这个细节之外,我之前的代码没有任何变化或特殊调整。

当前数据库的加载时间,现在4.8mb从11分钟下降到13秒。