使用NHibernate加速批量插入操作

时间:2012-03-30 13:30:15

标签: .net performance oracle nhibernate orm

我希望在Oracle 11g上加速NHibernate 3.2的批量insert操作。为此,我尝试了

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加速批量插入(不使用statlese会话)?

6 个答案:

答案 0 :(得分:36)

以下应该有效,

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

答案 1 :(得分:4)

答案 2 :(得分:4)

以上所有提示都非常有效且非常有用。想要在集合中添加一个:禁用日志记录。在控制台中显示您的SQL会使您显着减慢,使用NHProf进行性能分析,自动注释以及通过NLog或log4net记录的SQL非常格式化。在我们的案例设置中:

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

将我们的批量插入时间从约6秒减少到略超过1秒。因此,虽然日志记录可能会帮助您解决更严重的问题,但它会带来性能损失!

答案 3 :(得分:1)

为什么要清算会话?

我认为你不应该在循环中清除会话。为了确保将更改写入数据库,我宁愿使用事务。

伪代码:

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

为了加快速度,还有其他一些可能会有所帮助的事情 - 你必须在循环中定义你真正想做的事情。

答案 4 :(得分:0)

我知道问题是关于Oracle但是对于SQL服务器我打算编写一个例程来获取类映射并生成一个由SqlBulkInsert使用的DataTable,但我发现有人已经这样做了。

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

这可能是在SQL Server上进行批量插入的最快方法。

答案 5 :(得分:0)

以下代码适用于我插入多个复合实体

 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对象。