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