为什么没有事务的1M记录插入比事务内部慢?

时间:2009-06-25 10:01:28

标签: .net sql-server database transactions

我正在使用.Net 3.5对SQL Server进行一些性能测试。我正在插入100万条记录。当我将它包装在一个事务(可序列化,RepeatabelRead或ReadUncommited)中时,它在我的系统上运行不到80秒。当我删除它在大约300秒内运行的事务。我希望不使用事务是将行插入数据库的最快方法,因为DBMS不需要考虑潜在的回滚。这里发生了什么?这是典型的SQL Server,SQL Server ADO.Net Provider,ADO.Net一般,DBMSes一般吗?

我有iSeries / DB2数据库的背景知识。在DB2中,您必须先启用日记功能,然后才能获得承诺控制和事务处理,并且日记功能相对昂贵。

我真正想做的是比较SqlCommand插入与实体框架插入,但我对这些结果感到非常惊讶,我想先找出这里的内容。

在我用来运行测试的代码下面。当我运行以下代码时,大约需要74秒(在AtStart日志和AtEnd日志行之间测量)

using (SqlConnection sqlConnection = new SqlConnection(connectionString))
{
    sqlConnection.Open();
    SqlCommand deleteCommand = new SqlCommand("DELETE FROM LockTest");
    deleteCommand.Connection = sqlConnection;
    deleteCommand.ExecuteNonQuery();

    using (SqlTransaction transaction = sqlConnection.BeginTransaction(System.Data.IsolationLevel.Serializable))
    {
        try
        {
            if (DEBUG) LOG.Debug("AtStart");

            SqlCommand insertCommand = new SqlCommand();
            insertCommand.Connection = sqlConnection;
            insertCommand.Transaction = transaction;

            insertCommand.CommandText = "INSERT INTO LockTest (Id, Name, Description, Type) "  + 
                "VALUES (@id, @name, @description, @type)";
            SqlParameter idParameter = new SqlParameter("@id", System.Data.SqlDbType.UniqueIdentifier);
            insertCommand.Parameters.Add(idParameter);
            SqlParameter nameParameter = new SqlParameter("@name", System.Data.SqlDbType.NVarChar, 50);
            insertCommand.Parameters.Add(nameParameter);
            SqlParameter descriptionParameter = new SqlParameter("@description", System.Data.SqlDbType.NVarChar, Int32.MaxValue);
            insertCommand.Parameters.Add(descriptionParameter);
            SqlParameter typeParameter = new SqlParameter("@type", System.Data.SqlDbType.NChar, 20);
            insertCommand.Parameters.Add(typeParameter);

            insertCommand.Prepare();

            for (int i= 0; i < 1000000; i++)
            {
                Guid g = Guid.NewGuid();
                string s = g.ToString();
                insertCommand.Parameters["@id"].Value = g;
                insertCommand.Parameters["@name"].Value = s;
                insertCommand.Parameters["@description"].Value = DateTime.UtcNow.Ticks.ToString();
                insertCommand.Parameters["@type"].Value = "test";
                insertCommand.ExecuteNonQuery();
            }
            transaction.Commit();
        }
        catch
        {
            transaction.Rollback();
            throw;
        }

    }
    sqlConnection.Close();
}
if (DEBUG) LOG.Debug("AtEnd");

4 个答案:

答案 0 :(得分:8)

记录同花顺。

如果没有显式事务,则每个语句(即INSERT)启动的隐式事务必须提交。在将日志中的数据写入磁盘之前,Commit无法返回,这意味着每个INSERT语句都必须等待日志磁盘写入操作。

显式事务只能在发出COMMIT语句时等待,到那时每个完整的日志页面都已提交,最后一个日志页面可能包含几个INSERT,因此写入的成本是分摊的。

更新

您可以在效果计数器中验证日志刷新时间:http://msdn.microsoft.com/en-us/library/ms189883.aspx

  • 日志刷新等待时间刷新日志的总等待时间(以毫秒为单位)。
  • 日志刷新等待/秒等待日志刷新的每秒提交次数。
  • Log Flushes / sec 每秒的日志刷新次数。

答案 1 :(得分:0)

因为每个命令(如果未明确设置事务)隐式包装事务,即您有1M事务。至少对于sqLite

答案 2 :(得分:0)

如果您不是事务性的,它必须获取并释放每个插入的锁。通过该事务,它可以为多个插入保持锁定。开销减少。

答案 3 :(得分:0)