SqlDataAdapter.Fill() Тайм-аут - базовый Sproc быстро возвращается
У меня есть SqlDataAdapter, который заполняется 21 строк данных (4 столбца). Sproc, который управляет им, возвращается через пару секунд в SQL Mgmt Studio, но.Fill() занимает 5 минут.
ArrayList ret = new ArrayList();
SqlDataAdapter da = null;
SqlCommand cmd = null;
cmd = base.GetStoredProc("usp_dsp_Stuff"); //Returns immediately in MSSMS.
cmd.CommandTimeout = 3600; // Set to 6 min - debug only
base.AddParameter(ref cmd, "@Param1", ParameterDirection.Input, SqlDbType.BigInt, 8, 19, 0, theParam1);
base.AddParameter(ref cmd, "@Param2", ParameterDirection.Input, SqlDbType.BigInt, 8, 19, 0, theParam2);
base.AddParameter(ref cmd, "@Param3", ParameterDirection.Input, SqlDbType.Char, 1, 'C');
da = new SqlDataAdapter(cmd);
DataTable dt = new DataTable();
da.Fill(dt); //Takes 5 minutes.
Есть идеи?
Заранее спасибо! -Крис
10 ответов
da = new SqlDataAdapter(cmd);
da.SelectCommand.CommandTimeout = 1800;
Недавно я испытал именно это: .Fill
тайм-аут, но тот же SP был сверхбыстрым в SQL Server Management Studio. Это потому, что ваше приложение.NET создает соединение SQL и использует SET ARITHABORT OFF
тогда как SQL Server Management Studio использует SET ARITHABORT ON
по умолчанию. Это приводит к использованию двух разных планов выполнения, поэтому вы не смогли воспроизвести это время ожидания в SQL Server Management Studio. Я рекомендую вам взглянуть на ваш SP и внести некоторые изменения.
Спасибо вам за помощь. Решением этой проблемы было добавить операторы (nolock) для объединений, которые использовал sproc:
FROM category_tbl c ВНУТРЕННИМ СОЕДИНЕНИЕМ dbo.categoryItem_LNK cl WITH (NOLOCK) ON c.categoryid = cl.categoryid
Я не знаю, почему мы видели только ухудшение качества при использовании SqlDataAdapter, но это изменило, решило проблему сразу.
Еще раз спасибо, Крис
Я знаю, что это слишком поздно, как 7 лет слишком поздно! но я столкнулся с этой проблемой сегодня и хотел поделиться своим решением. В моем случае данные, извлеченные из SQL, представляли собой табличную функцию. Функция с табличными значениями возвратила только около 3500 строк и заняла менее 1 секунды, но по тайм-ауту она была вызвана функцией Fill () в коде C#. Я не знаю, кто или как это работает, но удаление и повторное создание функции исправили это. Я думаю, что это как-то связано с тем, как.NET читает данные, предоставляемые SQL, например, способ, которым необходимо воссоздать представление, если вы вносите в него изменения после того, как он используется в отчете. Опять же, я не уверен на 100%, что происходит за кулисами, но для меня это было быстрое решение
Плохие планы запросов и сниффинг параметров. Для хранимой процедуры, и особенно той, где параметры будут дико корректировать прочитанные строки, причиной является плохой план выполнения из-за просмотра входящих параметров. Это не происходит в SQL Management Studio из-за различных параметров SET.
В этой ветке вы можете подытожить вашу проблему: http://social.msdn.microsoft.com/Forums/en-US/transactsql/thread/9fd72536-f714-422a-b4c9-078e2ef365da/
Это типичный случай перехвата параметров. Скорее всего, ваше приложение работает с другими параметрами SET (устанавливаемыми клиентским API) и использует другой план выполнения, чем тот, который создан в SSMS. Что происходит, когда ваша процедура вызывается в первый раз через ваше приложение, создает план выполнения на основе переданных параметров. Однако этот план выполнения может не подходить для другого набора параметров, что может привести к снижению производительности при выполнении с другим набором параметров. Для получения дополнительной информации и различных решений см. Следующее: http://pratchev.blogspot.com/2007/08/parameter-sniffing.html
Вот больше о внутренностях кэширования плана и повторного использования плана запроса:
http://technet.microsoft.com/en-us/library/cc966425.aspx
Я ненавижу рассказывать новости, но (NOLOCK) не является решением, оно просто создает новые проблемы, такие как грязное чтение, пропущенные / дублированные данные и даже прерванные запросы. Блокировки в базе данных SQL - ваш друг.
Если блокировка (или, что еще хуже, блокировка) вызывала медленную работу, вы сравниваете параметры подключения, работающие через SSMS, и параметры, используемые вашим приложением. Используйте SQL Profiler, чтобы увидеть, как выполняется код.
Если какое-либо из этих полей является крупным объектом, имейте в виду, что SSMS по умолчанию автоматически получает только несколько сотен символов. Возвращенные дополнительные данные могут быть фактором.
Я использовал следующий код, и его продолжительность — sqlCommclass.CommandTimeout, которую я добавил перед da.Fill (dt), поскольку время запроса превышало 10 минут.
using (SqlConnection myADONETConnection = new SqlConnection(vendor.Value))
{
using (SqlDataAdapter da = new SqlDataAdapter("", myADONETConnection))
{
..
..
..
da.SelectCommand = sqlCommclass;
sqlCommclass.CommandTimeout = 30000;
da.Fill(dt);
}
}
В моем случае проблема заключалась в блокировке соответствующей таблицы. (нолок) не помогло.
Другие решения не работали для меня. Что сработало, так это добавление
OPTION(RECOMPILE)
в конце моего запроса, как предлагается здесь: /questions/24496144/isklyuchenie-tajm-auta-dataadapterfillnabor-dannyih-v-konkretnoj-baze-dannyih/24496155#24496155
Fill() иногда может быть медленным, потому что.NET анализирует данные, которые возвращаются из процедуры.
Используйте SQL Profiler для определения того, что SQL .NET фактически отправляет при выполнении Fill().
Если он отправляет много операторов SET, таких как
установить concat_null_yields_null на выключить cursor_close_on_commit отключить implicit_transactions так далее...
... тогда добавление тех же самых операторов set в вашу хранимую процедуру может ускорить процесс.