NHibernate列表保存和加载优化建议

时间:2011-10-03 17:56:39

标签: nhibernate nhibernate-mapping

我有3个通过NHibernate映射的类:交集,车辆和区域。

My Intersection类包含属于Intersection的区域列表。 My Vehicle类包含Vehicle包含的区域列表。最后,我的Zone类包含一个包含Zone的车辆列表。

我的测试性能集包含10,000个车辆和500个区域以及250个交叉点。对于所有这些对象,我对载有列表的区域和车辆的加载时间大约为27分钟。

我不确定发生了什么,但这两个列表至少没有优化。如果我从Vehicle类映射中取出Vehicle列表,则保存对象有10分钟的差异。看起来有点像2个列表是如何直接相互关联的。

似乎NHibernate以递归方式保存两个列表中的项目,并为保存过程添加了大量开销。有没有办法优化这些列表,以便更快地保存对象?

以下是我的设备的映射,我的交叉口和车辆继承自:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Devices.Device, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Device`">
    <id name="PK" type="System.Int64, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PK" />
      <generator class="identity" />
    </id>
    <version name="LastModifiedOn" column="LastModifiedOn" type="timestamp" access="field.pascalcase-underscore" />
    <joined-subclass name="EMTRAC.Intersections.Intersection, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <key>
        <column name="Device_id" />
      </key>      
      <component name="Zones" access="property">
        <bag name="_list" cascade="all-delete-orphan" access="field" fetch="join" inverse="false">
          <key>
            <column name="Zone_PK" />
          </key> 
          <many-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </bag>
      </component>
      <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="ID" />
      </property>
    </joined-subclass>
    <joined-subclass name="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <key>
        <column name="Device_id" />
      </key>      
      <component name="Zones" access="property">
        <bag name="_list" cascade="save-update" access="field" table="VehicleZones" inverse="true">
          <key>
            <column name="veh_id" not-null="true"/>
          </key>
          <many-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </bag>
      </component>
      <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="ID" />
      </property>           
    </joined-subclass>
  </class>
</hibernate-mapping>    

最后这是我对Zone类的映射:

    <hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Zone`">
    <id name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PK"/>
      <generator class="identity" />
    </id>
    <version name="LastModifiedOn" column="LastModifiedOn" type="timestamp" access="field.pascalcase-underscore" />
    <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="ID" />
    </property>    
    <component name="Vehicles" access="property">
      <bag name="_list" cascade="save-update" access="field" table="VehicleZones">
        <key>
          <column name="veh_id" not-null="true"/>
        </key>
        <many-to-many class="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </bag>
    </component>
  </class>
</hibernate-mapping>

如何提高这些列表的效率?现在,它保存它们的每个表包含500,000条记录,当然加载它们并保存许多记录需要一些时间。

修改

我删除了所有忘记删除的懒惰=“假”部分,而且速度要快得多。我还对保存和加载对象进行了一些调整,将部件分成单独的线程以提高性能,并在NHibernate中实现事务的使用似乎有所帮助。

但是我遇到了一个问题。我现在无法保存这两个列表。在将列表添加到车辆和区域之前,我已预先保存了所有交叉路口,车辆和区域。但是,如果我在区域映射中包含车辆列表,则在尝试更新车辆和列表时会出现错误。这是代码:

using (var tx = session.BeginTransaction())
{
    foreach (Vehicle vehicle in Program.data.Vehicles.list)
    {
        session.Update(vehicle);
    }
    tx.Commit();
}

我收到Stack Overflow异常。有什么想法吗?

知道如何让它通过吗?

修改的 是否无法将Zone类中的Vehicle列表和Vehicle类中的Zones映射到同一个表,因此它不会递归地迭代这两个列表?这些清单彼此相关。一个是车辆所属的区域列表,另一个是区域所属的车辆列表。在深入研究之后,我认为没有正确映射。

修改的 我一直在用我认为正确的映射取得进展。我发布了一个更简化版本的Device映射,它映射了Intersection和Vehicle类以及Zone的简化版本,当然它映射了Zone类。

除了一个例外,这似乎相对较好。由于某种原因,保存大量项目会导致Stack Overflow异常。这是我正在使用的方法:

//  Create a Transaction for batch updating
using (var tx = session.BeginTransaction())
{
    foreach (Vehicle veh in Program.data.Vehicles.list)
    {
        session.Save(veh);
    }
    //  Commit transactions
    tx.Commit();
}

这适用于250个交叉路口,500个区域和1000个车辆。但是,我试图创建一个测试数据集以在更极端的环境中强调这一点并且碰到2400个交叉点,9600个区域和5000个车辆,并且当我尝试将测试数据保存到数据库通过这种方式。

有什么想法吗?

3 个答案:

答案 0 :(得分:0)

虽然不是答案,但我建议NHibernate不是保存500K记录的最佳解决方案,尽管我接受有时很难用(例如)存储过程替换那段代码。

您可以考虑无状态会话,因为它针对速度进行了优化,但不跟踪对象更改。另一件事是找出正在执行的SQL。您是否看到了您不期望的更新?在这种情况下,您的映射文件可能存在错误。

最后,如果你做了很多数据更改,那么NH需要将它们刷新到数据库,并且需要跟踪加载的对象以跟踪这些更改。在500K记录中,这是要保留在内存中的大量数据。

答案 1 :(得分:0)

  

似乎NHibernate以递归方式保存两者中的项目   列表

你不必猜; nhibernate有great logging mechanisms,它可以准确显示sql的生成内容。看看它究竟是做什么并将其发回这里进行分析可能是一个好主意。

其次,nHib并非真正用于这些数据量 看到我收到的建议here 我能给你的最好的建议就是我收到的同一个 - 不要一次将10,000个对象加载到内存中!这不是一个好的做法,不管你使用什么数据访问方法。

答案 2 :(得分:0)

它实际上是以递归方式保存它们b / c我的映射不正确。我需要在一侧有反向关系,然后我需要修改映射以指向外键。最后我需要一个Modified字段来告诉NHibernate项目已被修改并且需要更新。

这是正确的映射:

车辆:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Devices.Device, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Device`">
    <id name="PK" type="System.Int64, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PK" />
      <generator class="identity" />
    </id>
    <version name="LastModifiedOn" column="LastModifiedOn" type="timestamp" access="field.pascalcase-underscore" />
    <joined-subclass name="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null">
      <key>
        <column name="Device_id" />
      </key>      
      <component name="Zones" access="property">
        <bag name="_list" cascade="save-update" access="field" table="VehicleZones" inverse="true">
          <key>
            <column name="veh_id" not-null="true"/>
          </key>
          <many-to-many class="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
        </bag>
      </component>
      <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
        <column name="ID" />
      </property>     
  </class>
</hibernate-mapping>

区域:

<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2">
  <class xmlns="urn:nhibernate-mapping-2.2" name="EMTRAC.Zones.Zone, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null" table="`Zone`">
    <id name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="PK"/>
      <generator class="identity" />
    </id>
    <version name="LastModifiedOn" column="LastModifiedOn" type="timestamp" access="field.pascalcase-underscore" />
    <property name="ID" type="System.Int32, mscorlib, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089">
      <column name="ID" />
    </property>    
    <component name="Vehicles" access="property">
      <bag name="_list" cascade="save-update" access="field" table="VehicleZones">
        <key>
          <column name="veh_id" not-null="true"/>
        </key>
        <many-to-many class="EMTRAC.Vehicles.Vehicle, EMTRAC_v3, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
      </bag>
    </component>
  </class>
</hibernate-mapping>