C #, Sql Server 2008: потоковый большой набор результатов для конечного пользователя работает только на некоторых базах данных - PullRequest
4 голосов
/ 16 сентября 2010

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

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

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

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

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

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

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

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

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

Разъяснение, основанное на ответе ниже

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

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

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

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

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

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

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

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

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

Ответы [ 2 ]

3 голосов
/ 17 сентября 2010

Все, что я знаю, говорит о том, что то, что вы делаете, должно работать; и 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 для открытия, браузер кэширует весь файл перед вызовом стороннего приложения.

0 голосов
/ 16 сентября 2010
  1. Поместите новую задачу, которая собирает этот огромный CSV-файл, в таблицу задач.
  2. Запустите процедуру для обработки этой задачи.
  3. Подождите, пока результат появится в таблице задач с SqlDependency.
  4. Вернуть результат клиенту.
...