У меня есть 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 транспортных средств, и я столкнулся с исключением переполнения стека при попытке сохранить тестовые данные в база данных через этот способ.
Есть идеи?