2

在程序启动时,我将整个数据库的完整对象图(没有延迟加载)加载到域对象中。我知道这不是典型的用法,但这不在问题范围内。数据库只能由单个用户/程序访问。下面的条件是关于这个单一的启动负载操作;

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

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. 任何改善加载时间的相关建议

编辑:

映射;

   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)().List(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();

我不使用任何日志框架,所以认为不应该打开日志。虽然我目前确实运行调试版本,但应检查发布版本是否有任何不同。

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

顺便说一句,我正在使用NHibernate 3.1.0.4

4

3 回答 3

0

一些建议/问题

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

如果一切都失败了:

  • 尝试 SQL CE 4

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

于 2013-07-31T00:02:32.697 回答
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 并删除不必要的匿名代表
  • 延迟 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();
    
于 2013-07-31T06:12:14.830 回答
0

最后发现我只需要改变一件事;

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

HasMany(x => x.Orders)。Fetch.Subselect() .KeyColumn("Account__Id").Not.LazyLoad().Cascade.All().Inverse();

(我也添加了Not.LazyLoad()那里,但没有区别,所以可以省略)

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

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

于 2013-09-02T23:16:46.357 回答