2009-06-25 3 views
3

Я выполняю некоторые тесты производительности с использованием .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"); 
+1

Транзакция уровень изоляции по определению влияет только на чтение. Записи (т.е. INSERTS) ведут себя одинаково на всех уровнях изоляции. –

+0

Стол очищается между каждым прогоном? Согласован каждый раз? – gbn

+0

Вы можете использовать TRUNCATE TABLE вместо DELETE FROM для подготовки тестов кстати. И чтобы быть полностью точным, вы должны каждый раз создавать базу данных с нуля, следя за тем, чтобы вы предварительно расширили файлы данных и журналов (выделите начальные размеры, достаточно большие для теста). Событие с одной базой данных или журнальным событием во время одного из прогонов сбросит все результаты для этого запуска. –

ответ

8

Журнал флеш.

Без явных транзакций неявные транзакции, начатые каждым оператором (т.е. 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 Количество журнальных флешей в секунду.
+0

Спасибо за информацию до сих пор: как насчет второй части моего вопроса. Является ли это типичным для SQL Server или другие СУБД ведут себя одинаково, например. MySQL, Oracle? –

+0

Это типично для всех баз данных, основанных на записи журнала (http://en.wikipedia.org/wiki/Write_ahead_logging). MySQL ведет себя так же с движком InnoDB и с Oracle (может быть, всевозможные регуляторы для управления этим в Oracle, я отнюдь не эксперт Oracle). Альтернативой WAL является версия paging (http://en.wikipedia.org/wiki/Shadow_paging), но единственная коммерческая БД, которую я знаю о развертывании, это Informix. Системы, которые не используют WAL или версии страниц, не предлагают ACID, поэтому они обычно не предлагают транзакции (например, MySQL ISAM engine). –

0

Поскольку каждая команда (если транзакция не выставиться явно) обматывают сделки неявно т.е. вы имеете 1M сделки , По крайней мере, для sqLite

0

Он должен получить и освободить замок для каждой вставки, если вы не являетесь транзакционным. С транзакцией он может держать блокировку открытой для нескольких вставок. Меньше накладных расходов.

+0

Количество блокировок, полученных/выпущенных, идентично в обоих случаях. –

+0

Это не зависит от детализации приобретенных замков? –

+0

@Remus: возможно, нет. Это приведет к эскалации блокировки на странице/таблице. – gbn

Смежные вопросы