Большие выгрузки SQL Server без переполнения памяти клиента
Ограничивайте память клиента, исследуйте ASYNC_NETWORK_IO и отделяйте чтение базы от медленной загрузки файла с явной проверкой завершенности.
Запрос на миллионы строк не завершен для пользователя в момент, когда SQL Server нашел первую строку. Данные еще нужно передать, декодировать, отформатировать, записать и доставить. Быстрый план может сочетаться с медленной выгрузкой и нехваткой памяти приложения.
Измерить весь путь результата
Записывайте время первой строки, окончания чтения, окончания записи, число строк, объем файла и пик памяти. Это разделяет вычисления сервера, передачу и форматирование. Таймер вокруг первого Read не измеряет всю выгрузку.
Во время медленного выполнения следующий читающий запрос показывает активную работу при наличии нужных диагностических прав.
SELECT
r.session_id, r.request_id,
r.status, r.wait_type, r.wait_time,
r.total_elapsed_time, r.cpu_time,
r.logical_reads, r.reads, r.row_count,
s.program_name, s.host_name
FROM sys.dm_exec_requests AS r
JOIN sys.dm_exec_sessions AS s ON s.session_id = r.session_id
WHERE s.is_user_process = 1
AND r.session_id <> @@SPID
ORDER BY r.total_elapsed_time DESC;
Повторяющееся ASYNC_NETWORK_IO означает ожидание получения результатов или связанного продвижения сети. Оно не доказывает поломку сети. Клиент, который сжимает, журналирует или вызывает HTTP после каждой строки, может читать медленно при исправном соединении. Сопоставьте CPU приложения, скорость записи и сетевые показатели.
Уберите ненужные данные у источника. Выбирайте нужные столбцы и переносите корректные фильтры либо агрегации в SQL. Большое описание, отброшенное позже, все равно требует передачи и декодирования. Не меняйте смысл: замена подробной выгрузки итогами дает другой продукт.
Ограничить память и учитывать обратное давление
Однонаправленный reader позволяет не собирать результат целиком. Для больших бинарных и текстовых полей SequentialAccess и потоковые API SqlClient поддерживают постепенное чтение. Соблюдайте порядок доступа к столбцам и завершайте чтение потокового значения до перехода дальше согласно этому режиму.
Асинхронный ввод-вывод сам не ограничивает память. Создание задачи на каждую строку с сохранением всех задач или объектов продолжает рост вместе с набором. Используйте ограниченный канал или фиксированные партии между извлечением и преобразованием, с ограниченным числом работников.
При замедлении следующей стадии буфер должен прекратить рост. Однако такое обратное давление держит reader и соединение открытыми. В зависимости от запроса и изоляции долгая работа может продлить блокировки, удержать версии или резерв памяти. Чтение всего в память быстрее освобождает соединение, но переносит давление на приложение.
Для медленной пользовательской загрузки фоновое задание может записать временный файл в контролируемое хранилище и закрыть reader до скачивания. Публикуйте окончательный файл только после успеха, сохранив число строк, размер и желательно контрольную сумму. Прерванный файл не должен выглядеть полным.
Определить согласованность и завершение
Решите, какой момент данных представляет результат. Несколько независимых частей при read committed не образуют автоматически единый снимок. Между ними строки меняются. Долгая snapshot-транзакция дает другой контракт, но может удерживать версии. Документируйте семантику и измеряйте ее стоимость.
Если файл должен иметь воспроизводимый порядок или поддерживать продолжение, задайте стабильную сортировку. ORDER BY может добавить работу серверу. Верхняя граница ключа сама по себе не гарантирует неизменность значений во время выгрузки.
Передавайте отмену и освобождайте reader, command и собственное соединение на всех выходах. Явно завершайте принадлежащую заданию транзакцию. Проверьте отключение потребителя посередине, заполнение диска, позднюю ошибку преобразования и повтор того же задания.
Различайте завершение задания и успешное скачивание. Готовый корректный файл может существовать даже после разрыва пользовательского соединения. Его повторная выдача обычно дешевле нового запроса. Добавьте в состояние задания ссылку на окончательный артефакт и проверенные показатели, чтобы интерфейс не угадывал готовность по наличию файла. Надежная выгрузка имеет ограниченные ресурсы и явный сигнал полноты, а не только цикл, который перестал получать строки.
Техническая документация: Microsoft Learn: ASYNC_NETWORK_IO · Microsoft Learn: SqlClient streaming.