Ускорьте операции массовой вставки с помощью NHibernate

Я хочу ускорить объем insert операции с NHibernate 3.2 на Oracle 11g. Для этого я пытался

Session.Save(entity);
Session.Flush();
Session.Clear();

... в моем foreach цикл, но получил исключение, вызванное отсутствием объектов в сеансе:

не удалось лениво инициализировать коллекцию ролей: MyClass.PropertyX, ни один сеанс или сеанс не был закрыт

Еще одна попытка была установить размер пакета:

<hibernate-configuration xmlns="urn:nhibernate-configuration-2.2">
  <session-factory>
    <property name="connection.provider">NHibernate.Connection.DriverConnectionProvider</property>
    <property name="connection.driver_class">NHibernate.Driver.OracleClientDriver</property>
    <property name="connection.connection_string">xxx</property>
    <property name="dialect">NHibernate.Dialect.Oracle10gDialect</property>
    <property name="adonet.batch_size">50</property>
    <property name="query.substitutions">true=1, false=0</property>
    <property name="proxyfactory.factory_class">NHibernate.Bytecode.DefaultProxyFactoryFactory, NHibernate</property>
  </session-factory>
</hibernate-configuration>

дополнительно я установил Session.SetBatchSize(50) в моем коде есть следующее исключение:

Для фабрики сеансов размер пакета не был определен, пакетирование отключено. Установите adonet.batch_size = 1, чтобы включить пакетирование.

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

Что здесь не так? Как ускорить пакетную вставку с помощью NHibernate (без использования статических сеансов)?

6 ответов

Следующее должно работать,

var testObjects = CreateTestObjects(500000);

var stopwatch = new Stopwatch();
stopwatch.Start();
using (IStatelessSession session = sessionFactory.OpenStatelessSession())
using (ITransaction transaction = session.BeginTransaction())
{
    foreach (var testObject in testObjects)
        session.Insert(testObject);
    transaction.Commit();
}

stopwatch.Stop();
var time = stopwatch.Elapsed;

Ссылка: http://nhibernate.info/blog/2008/10/30/bulk-data-operations-with-nhibernate-s-stateless-sessions.html

Все вышеперечисленные советы очень актуальны и очень полезны. Хотел добавить один в коллекцию: отключить ведение журнала. Отображение вашего SQL в консоли заметно замедляет вас, также как и профилирование с использованием NHProf, автоматическое комментирование и красивое форматирование SQL, записанного в журнал через NLog или log4net. В нашем случае настройка:

cfg.AutoCommentSql = false;
cfg.LogFormattedSql = false;

уменьшено время массовой вставки с ~6 секунд до чуть более 1 секунды. Таким образом, хотя ведение журнала потенциально может помочь вам решить более серьезные проблемы, оно само по себе идет на снижение производительности!

Почему вы очищаете сессию?

Я думаю, что вы не должны очищать сессию в цикле. Чтобы убедиться, что изменения записаны в базу данных, я бы предпочел использовать транзакцию.

псевдокод:

foreach (var i in allElements)
{
    using (var tx = session.BeginTransaction())
    {
        ... do what you have to do with the object
        tx.Commit();
    }
}

Чтобы ускорить процесс, есть и другие вещи, которые могут помочь - вы должны определить, что вы действительно хотите сделать в цикле.

Я знаю, что вопрос был об Oracle, но для SQL-сервера я собирался написать подпрограмму для отображения отображений классов и создания DataTable для использования SqlBulkInsert, но я обнаружил, что кто-то уже сделал это.

https://kaylaniam.wordpress.com/2015/03/13/nhibernate-and-sqlbulkcopy/

Это, вероятно, самый быстрый способ выполнения массовых вставок в SQL Server.

Код ниже работает для меня на вставку нескольких составных объектов

 public static void SqlBulkInsert(this ISession session, DataTable dataTable, string tableName)
    {
        var conn = (SqlConnection)session.Connection;
        using (var cmd = new SqlCommand())
        {
            session.Transaction.Enlist(cmd);
            using (var copy = new SqlBulkCopy(conn, SqlBulkCopyOptions.FireTriggers, cmd.Transaction))
            {
                copy.BulkCopyTimeout = 10000;
                copy.DestinationTableName = tableName;
                foreach (DataColumn column in dataTable.Columns)
                {
                    copy.ColumnMappings.Add(column.ColumnName, column.ColumnName);
                }

                copy.WriteToServer(dataTable);
                copy.Close();
            }
        }
    }

Вам нужно будет создать метод для заполнения объекта DataTable из объекта составной сущности, который вы хотите сохранить.

Другие вопросы по тегам