【发布时间】:2009-06-25 10:01:27
【问题描述】:
我正在使用 .Net 3.5 针对 SQL Server 进行一些性能测试。我正在插入 100 万条记录。当我将它包装在一个事务(可序列化、RepeatabelRead 或 ReadUncommited)中时,它在我的系统上运行时间不到 80 秒。当我删除事务时,它会在大约 300 秒内运行。我希望不使用事务将是向数据库中插入行的最快方法,因为 DBMS 不需要考虑潜在的回滚。这里会发生什么?这对于 SQL Server、SQL Server ADO.Net 提供程序、一般的 ADO.Net、一般的 DBMS 来说是典型的吗?
我有 iSeries/DB2 数据库方面的背景。在 DB2 中,您必须先启用日志,然后才能获得提交控制和事务,而且日志相对昂贵。
我真正想做的是比较 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");
【问题讨论】:
-
根据定义,事务隔离级别只影响读取。写入(即 INSERTS)在所有隔离级别上的行为都相同。
-
每次运行之间是否清除表?每次都一致?
-
您可以使用 TRUNCATE TABLE 而不是 DELETE FROM 来准备测试。为了完全准确,您应该每次都从头开始创建数据库,确保预先增长数据和日志文件(分配足够大的初始大小以进行测试)。在其中一次运行期间的单个数据库或日志增长事件将丢弃该运行的所有结果。
-
@Remus:如果我发现运行之间的差异相对较小,你是对的,我现在发现的是一个数量级的差异。
标签: .net sql-server database transactions