SqlCommand.Cancel() вызывает повышение производительности?

Я видел, как это показывает несколько мест в коде, без объяснений, просто загадочный комментарий над ним (объявление и выполнение включены для идеи контекста. Это просто стандартная процедура запуска SqlCommand):

//SqlCommand cmd = new SqlCommand();
//cmd.ExecuteReader();
//Read off the results

//Cancel the command. This improves query time.
cmd.Cancel ();

По сути, после завершения запроса он возвращается и отменяет его, требуя некоторого повышения производительности. Я полагаю, вы можете вернуть некоторую память, когда она выйдет и освободит XmlReader, но обычно она все равно выходит из области видимости.

Я никогда не беспокоился об этом раньше, но он наконец-то обнаружился в некотором коде, который я рецензирую. Отменяет ли SqlCommand после запуска его в коде какое-то ускорение или это просто какое-то странное суеверие программиста?

4 ответа

Решение

Согласно MSDN, это правильно.

Метод Close заполняет значения для выходных параметров, возвращаемых значений и RecordsActed, увеличивая время, необходимое для закрытия SqlDataReader, который использовался для обработки большого или сложного запроса. Если возвращаемые значения и количество записей, затронутых запросом, не являются значительными, время, необходимое для закрытия SqlDataReader, можно уменьшить, вызвав метод Cancel связанного объекта SqlCommand перед вызовом метода Close.

Weird!

Призвание Cancel дает потенциально ОГРОМНОЕ улучшение производительности, если ваш звонок ExecuteReader возвращает большое количество строк, и вы не читаете все строки.

Для иллюстрации, скажем, запрос возвращает миллион строк, и вы закрываете читателя после прочтения только первых 1000 строк. Если вы не можете позвонить Cancel перед закрытием читателя Close метод будет блокироваться, пока он внутренне перечисляет оставшиеся 999 000 строк

Попробуйте и посмотрите!

Наша техническая команда в Cinchcast провела некоторое тестирование, и мы обнаружили, что добавление cmd.Cancel() на самом деле замедляет его.

У нас есть вызов DALC, который получает список эпизодов для хоста. Мы запустили его 1000 раз и получили среднее время отклика для возврата 10 эпизодов.

Итак, с возвратом 10 показывает Среднее с отменой: 0,069 с Среднее без отмены: 0,026 с

Довольно значительно медленнее при запуске с возвращением 10 эпизодов.

Итак, я попробовал еще раз с возвращением 100 эпизодов, чтобы увидеть, если большой набор результатов имеет значение.

То есть с возвратом 100 показов на каждый звонок. Среднее с отменой: 0,132 с. Среднее без отмены: 0,122 с.

Так что на этот раз разница во времени была намного меньше. Это все еще быстрее, хотя без использования Отмена для наших обычных случаев использования.

В вашем примере вы открываете считыватель, читаете все строки и команду "Отмена", но не показываете, где читатель был закрыт.

Убедитесь, что отмена происходит до Dispose/Close, Например, вы не получите повышение производительности в этом примере (реальный код в производстве, к сожалению):

using (var rdr = cmd.ExecuteReader (CommandBehavior.Default))
{
   retval = DocumentDir.DBRead (rdr);
}

// Optimization.  Allows reader to close more quickly.... NOT!
cmd.Cancel ();  // bad!

Жаль, что оно уже закрыто оператором использования!

Вот как это следует читать, чтобы понять потенциальную выгоду:

using (var rdr = cmd.ExecuteReader (CommandBehavior.Default))
{
   retval = DocumentDir.DBRead (rdr);

   // Optimization.  Allows reader to close more quickly.
   cmd.Cancel ();
}

Из MSDN SqlCommand.Cancel:

В некоторых, редких случаях, если вы вызываете ExecuteReader, затем вызываете Close (неявно или явно) перед вызовом Cancel, а затем вызываете Cancel, команда отмены не будет отправлена ​​на SQL Server, и результирующий набор может продолжить поток после вызова Close, Чтобы избежать этого, обязательно позвоните в "Отмена" перед тем, как закрыть ридер или соединение.

Другие вопросы по тегам