Советы по сохранению и загрузке списков NHibernate - PullRequest
0 голосов
/ 03 октября 2011

У меня есть 3 класса, которые отображаются через NHibernate: Пересечение, Автомобиль и Зона.

Мой класс пересечения содержит список зон, которые принадлежат пересечению. Класс My Vehicle содержит список зон, которые содержит Vehicle. Наконец, мой класс Zone содержит список транспортных средств, которые содержат зону.

Мой тестовый набор состоит из 10 000 транспортных средств и 500 зон и 250 пересечений. Мое время загрузки для обоих зон и транспортных средств с сопоставленными списками составляет около 27 минут для всех этих объектов.

Я не уверен, что происходит, но эти два списка не оптимизированы ни в малейшей степени. Существует 10-минутная разница в сохранении объектов, если я выберу список Транспортных средств из отображения класса Зоны. Это кажется немного странным, поскольку два списка напрямую связаны друг с другом.

Похоже, 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();
}

Я получаю исключение переполнения стека. Есть идеи?

Есть идеи, как заставить это пройти?

Редактировать Нет ли способа сопоставить список транспортных средств в классе Zone и список зон в классе Vehicle с одной и той же таблицей, чтобы он не рекурсивно просматривал оба списка? Списки связаны друг с другом. Одна - это список зон, к которым принадлежит транспортное средство, а другая - список транспортных средств, к которым принадлежит зона. Я не думаю, что у меня это правильно отображается после того, как копаться в этом.

EDIT Я делаю успехи с правильными отображениями, я верю. Я опубликовал более упрощенную версию сопоставлений для Устройства, которая отображает классы Пересечение и Транспортное средство вместе с упрощенной версией Зоны, которая, конечно, отображает класс Зоны.

Похоже, что это относительно неплохо экономит, за одним исключением. По какой-то причине сохранение большого количества элементов - это исключение исключения переполнения стека. Вот подход, который я использую:

//  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 голосов
/ 04 октября 2011

Похоже, NHibernate рекурсивно сохраняет элементы в обоих списках

вам не нужно угадывать;В nhibernate есть замечательных механизмов ведения журналов , которые точно показывают, что генерируется sql.Это может быть хорошей идеей, чтобы увидеть, что именно он делает, и опубликовать его здесь для анализа.

во-вторых, nHib на самом деле не предназначен для такого рода объемов данных.
см. Предложения, которые я получил здесь .
лучший совет, который я могу вам дать, такой жеодин я получил - не загружайте 10 000 объектов в память сразу!Это не очень хорошая практика, не важно, какой метод доступа к данным вы используете.

0 голосов
/ 10 октября 2011

Это было на самом деле рекурсивное сохранение их, потому что у меня было неправильное отображение. Мне нужно было обратное отношение с одной стороны, а затем мне нужно было изменить отображение, чтобы оно указывало также на внешний ключ. Наконец, мне нужно было поле 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>
0 голосов
/ 04 октября 2011

Хотя это и не ответ, я бы предположил, что NHibernate не является лучшим решением для сохранения записей размером 500 КБ, хотя я признаю, что иногда просто заменить этот фрагмент кода (например) хранимыми процедурами сложно.

Вы можете рассмотреть сеанс без сохранения состояния, поскольку он оптимизирован для скорости, но не отслеживает изменения объекта.Другое дело было бы узнать, какой SQL выполняется.Вы видите обновления, которые вы не ожидаете?В этом случае, вероятно, в файлах сопоставления есть ошибки.

В конце концов, если вы внесли много изменений в данные, то NH должен сбросить их в БД, и он должен отслеживатьзагруженные объекты для отслеживания этих изменений.При 500 тыс. Записей это много данных, которые нужно хранить в памяти.

...