C#, Sql Server 2008: поток большого набора результатов для конечного пользователя работает только с некоторыми базами данных

У меня есть длительный запрос, который возвращает большой набор данных. Этот запрос вызывается из веб-службы, и результаты преобразуются в CSV-файл для конечного пользователя. Предыдущие версии выполнялись более 10 минут и возвращали результаты конечному пользователю только после завершения запроса.

Я переписал запрос так, чтобы он выполнялся примерно через минуту в большинстве случаев, и переписал способ доступа к нему, чтобы результаты передавались клиенту по мере их поступления в веб-службу asp.net с сервера базы данных. Я проверил это, используя локальный экземпляр SQL Server, а также удаленный экземпляр без проблем.

Теперь, на пороге производственного развертывания, кажется, что наш производственный SQL-сервер не отправляет никаких результатов обратно в веб-службу, пока запрос не завершит выполнение. Кроме того, я обнаружил другую машину, идентичную работающему удаленному серверу (клоны), которая также не передает результаты.

Версия SQL Server 2008 одинакова на всех машинах. На рабочей машине установлена ​​немного другая версия Windows Server (6.0 против 6.1). Рабочий сервер имеет 4 ядра и в несколько раз больше оперативной памяти, чем другие серверы. Другие серверы являются одноядерными с 1 ГБ оперативной памяти.

Есть ли какие-либо настройки, которые могут вызвать это? Или есть ли какой-либо параметр, который я могу установить, чтобы SQL Server не буферизовал результаты?

Хотя я знаю, что это никак не повлияет на общее время выполнения, это сильно изменит восприятие конечного пользователя.

тл;др; Мне нужны результаты запроса для передачи конечному пользователю во время выполнения запроса. Он работает с некоторыми машинами баз данных, но не с другими. На всех машинах установлена ​​одна и та же версия SQL Server.

Суть того, что я делаю на С#:

var reader = cmd.ExecuteReader();
Response.Write(getHeader());
while(reader.Read())
{
  Response.Write(getCSVForRow(reader));
  if(shouldFlush()) Response.Flush()
}

Пояснение на основе ответа ниже

Есть 4 сервера баз данных, Local, Prod, QA1, QA2. Все они работают под управлением SQL Server 2008. На всех них загружены идентичные базы данных (более или менее, отставание на 1 день для непродуктивных).

Веб-служба размещена на моем компьютере (хотя я также тестировал удаленный хостинг).

Единственное изменение между тестами — это строка подключения в файле web.config.

QA2 работает (потоковая передача) и является клоном QA1 (виртуальные машины). Единственная разница между QA1 и QA2 — это добавленная база данных на QA2, никак не связанная с этим запросом.

QA1 не работает.

Все тесты включают набор данных максимального размера в результат (в настоящее время мы ограничиваем 5 тыс. строк). Браузер отображает диалоговое окно загрузки, как только происходит первый сброс. Это желаемый результат. Мы хотим, чтобы они знали, что их загрузка обрабатывается, даже если скорость загрузки низкая и временами падает до нуля (так бывает с базами данных).

Мой промывочный код на данный момент прост. Мы очищаем каждые k строк, при этом k в настоящее время установлено на 20.

Больше всего сбивает с толку тот факт, что QA1 и QA2 ведут себя по-разному. Я заметил, что наш рабочий сервер настроен на режим совместимости 2005 (90), где и QA, и локальная база данных установлены на 2008 (100). Я сомневаюсь, что это имеет значение. Когда я запускаю sprocs через SSMS, у меня одинаковое поведение на всех машинах. Я вижу поток результатов немедленно.

Есть ли какие-либо настройки строки подключения, которые могут отключить потоковую передачу?


person Tim Reynolds    schedule 16.09.2010    source источник


Ответы (2)


Все, что я знаю, говорит о том, что то, что вы делаете, должно работать; как DataReader, так и Response.Write()/.Flush() действуют "потоковым" способом и приведут к тому, что клиент будет получать данные по одной строке за раз, как только появятся строки для получения. Ответ действительно включает буфер, но вы отправляете буфер клиенту после каждой итерации чтения/записи, что сводит к минимуму его использование.

Я бы проверил, настроен ли веб-сервис для правильного ответа на команды Flush() из ответа. Убедитесь, что производственная среда не является установкой Win2008 Server Core; Windows Server 2008 не поддерживает Response.Flush() в некоторых ролях Server Core. Я бы также проверил, что условия, оцененные в ShouldFlush(), вернут true, когда вы ожидаете их в производственной среде (вы можете проверить конфигурацию приложения на предмет значения или посмотреть настройки IIS; я не знаю).

В вашем тесте я бы попробовал гораздо больший набор выборочных данных; может случиться так, что производственная среда выявляет проблемы, которые также присутствуют в тестовых средах, но с меньшим набором тестовых данных и высокоскоростной магистралью Ethernet проблема не заметна по сравнению с возвратом сотен тысяч строк через DSL. Вы можете убедиться, что он работает в потоковом режиме, вставив вызов Thread.Sleep() после каждого Flush(250); это замедлит выполнение службы и позволит вам наблюдать, как ответ передается вашему клиенту со скоростью 4 строки в секунду.

Наконец, убедитесь, что клиент, который вы используете в производственной среде, настроен на отображение CSV-файлов способом, допускающим потоковую передачу. В основном это означает, что веб-браузер, выступающий в роли клиента, не должен быть настроен на передачу файла стороннему приложению. Веб-браузер может легко отображать текстовый поток, передаваемый по протоколу HTTP; это то, что он делает, на самом деле. Однако, если он видит поток как CSV-файл и настроен на передачу CSV-файлов в Excel для открытия, браузер кэширует весь файл перед вызовом стороннего приложения.

person KeithS    schedule 16.09.2010
comment
Я собираюсь уточнить в своем исходном посте на основе вашего поста. Спасибо - person Tim Reynolds; 17.09.2010
comment
Отмечаю это как ответ, потому что, как говорит Кит, я делаю все, что должен, чтобы все заработало. Мы перешли к производству, и по причинам, которые я не могу объяснить, в производстве оно работает так, как ожидалось. Хотел бы я объяснить это, но я не могу. - person Tim Reynolds; 17.09.2010

  1. Поместите новую задачу, которая создает этот огромный CSV-файл, в таблицу задач.
  2. Запустите процедуру для обработки этой задачи.
  3. Подождите, пока результат появится в вашей таблице задач с помощью SqlDependency.
  4. Верните результат клиенту.
person Denis Valeev    schedule 16.09.2010
comment
В надежде определить, почему некоторые экземпляры SQL Server работают так, как я ожидаю, а другие нет. - person Tim Reynolds; 16.09.2010