Я выполняю некоторые тесты производительности с использованием .Net 3.5 для SQL Server. Я делаю вставку из 1 миллиона записей. Когда я переношу это внутри транзакции (либо сериализуемой, RepeatabelRead или ReadUncommited), она работает под 80 секунд в моей системе. Когда я удаляю транзакцию, она запускается примерно через 300 секунд. Я ожидал бы, что использование транзакции не будет самым быстрым способом вставки строк в базу данных, потому что в СУБД не нужно учитывать потенциальный откат. Что здесь происходит? Является ли это типичным для SQL Server, поставщика ADO.Net SQL Server, ADO.Net вообще, СУБД вообще?Почему вставка 1M записей медленнее без транзакции, чем внутри транзакции?
У меня есть опыт работы с базами данных iSeries/DB2. В DB2 вам необходимо включить публикацию журналов, прежде чем вы сможете получить контроль над транзакциями и транзакциями, а публикация журналов относительно дорога.
То, что я действительно хотел сделать, это сравнить вставки SqlCommand vs 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) ведут себя одинаково на всех уровнях изоляции. –
Стол очищается между каждым прогоном? Согласован каждый раз? – gbn
Вы можете использовать TRUNCATE TABLE вместо DELETE FROM для подготовки тестов кстати. И чтобы быть полностью точным, вы должны каждый раз создавать базу данных с нуля, следя за тем, чтобы вы предварительно расширили файлы данных и журналов (выделите начальные размеры, достаточно большие для теста). Событие с одной базой данных или журнальным событием во время одного из прогонов сбросит все результаты для этого запуска. –