.net - 为什么在没有事务的情况下插入 1M 记录比在事务中插入要慢?

标签 .net sql-server database transactions

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

我有 iSeries/DB2 数据库方面的背景知识。在 DB2 中,您必须先启用日志记录,然后才能获得 promise 控制和事务,而日志记录相对昂贵。

我实际上想做的是比较 SqlCommand 插入与 Entity Framework 插入,但我对这些结果感到非常惊讶,所以我想先找出这里发生了什么。

在我用来运行测试的代码下方。当我运行下面的代码时,大约需要 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");

最佳答案

日志刷新。

在没有显式事务的情况下,每个语句(即 INSERT)启动的隐式事务必须提交。 Commit只有在日志中的数据写入磁盘后才能返回,这意味着每条INSERT语句都必须等待日志磁盘写入操作。

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

更新:

您可以在性能计数器中验证日志刷新次数:http://msdn.microsoft.com/en-us/library/ms189883.aspx :

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

关于.net - 为什么在没有事务的情况下插入 1M 记录比在事务中插入要慢?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1043014/

相关文章:

C# MongoDB 驱动程序 : Can't find the way to run complex query for AnyIn filter in MongoDB

c# - .NET 中的 lambda 表达式和谓词有什么区别?

C# EF6 Code First TPH - 两个使用注释继承同一实体的实体的导航属性

database - 我应该使用什么工具在 Web 应用程序上实现 OrientDB?

php - 如何将PHP中数据库的多列求和为一个结果?那可能吗?

c# - 确定 .NET Windows 服务是否未挂起的最佳方法?

SQL:尝试在数据库的所有行中标记同一客户 ID 和订单号内的最早发货日期

sql - 如何根据另一个表中的记录数复制值

database - 存储和检索多个头像图像尺寸

.net - 时间戳是否有 Windows 绝对时间? (。网)