Ускорение скорости, с которой IIS/.NET/LINQ извлекает данные из сетевых буферов

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

Например, это размер сетевого буфера в байтах для одного из более долгоживущих подключений к серверу базы данных с течением времени:

Network Buffer Graph

На веб-серверах работает приложение .NET 4.0, работающее в интегрированном режиме IIS на веб-серверах Windows 2008 R2. Сервер SQL - сервер 2008 R2.

Моя интерпретация заключается в том, что сервер SQL возвращает данные на веб-сервер быстрее, чем приложение на веб-сервере может собирать данные из буферов. Я попытался настроить почти все, что могу, в сетевых драйверах, чтобы обойти эту проблему. В частности, увеличение очередей RSS, отключение модерации прерываний и установка сервера Windows 2008 R2 для более активного увеличения размера буфера.

Итак, если моя интерпретация верна, это оставляет мне интересно о двух возможностях:

  • Есть ли какой-нибудь способ в .NET рассказать об увеличении размера сетевых буферов? "Улучшенный стек TCP R2 2008" редко решает включить масштабирование окна (создание буфера более 65 кбайт) для этого соединения (вероятно, из-за низкой задержки). Похоже, что возможность установки этой системы вручную вручную исчезла на сервере Windows 2008 r2 (Раньше были записи в реестре, которые теперь игнорируются). Так есть способ, которым я могу заставить это в коде?
  • Есть ли что-нибудь, что можно настроить, что ускорит скорость, с которой приложение считывает информацию для сетевых буферов, в частности, для SQL-соединений?

Edit:
Запрошенный запрос DMV в ASYNC_NETWORK_IO:

SELECT * FROM sys.dm_os_wait_stats ORDER BY waiting_tasks_count desc;
wait_type  waiting_tasks_count  wait_time_ms  max_wait_time_ms  signal_wait_time_ms
CXPACKET            1436226309    2772827343             39259           354295135
SLEEP_TASK           231661274     337253925             10808            71665032
LATCH_EX             214958564     894509148             11855            84816450
SOS_SCHEDULER_YIELD  176997645     227440530              2997           227332659
ASYNC_NETWORK_IO     112914243      84132232             16707            16250951

Ответы

Ответ 1

1) Что заставляет вас думать, что это управление потоком TCP, в отличие от SQL Server, который не создает данные в промежутках, где нет трафика? Проверьте, есть ли sys.dm_exec_requests для параметра wait_type. Типы ожидания описаны в Ожидания и очереди. Если клиент действительно использует управление потоком TCP, то вы увидите тип ожидания ASYNC_NETWORK_IO.

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

Edit

Потребление набора результатов вызова БД в конечном итоге сводится к той или иной форме:

FetchNextRow
while (not EnfOfResults)
{
  ProcessRow;
  FetchNextRow;
}

Что это может означать, в реальном выражении это может быть foreach row in IQueryable или SqlDataReader.Read(). Но основная идея такая же, что клиент извлекает строки из результата, обрабатывает их, а затем получает еще несколько строк. Если клиентский код делает что-либо в этом ProcessRow, который блокирует, тогда код клиента не достигнет точки, где он снова извлекает следующую строку, и, таким образом, в конечном итоге вызовет управление потоком TCP, что, в свою очередь, заставит SQL Server приостановить запрос (так как нет смысла записывать результаты). Вы ничего не можете сделать с точки зрения TCP, чтобы сделать это лучше. Увеличение размера окна может на самом деле сделать матчи хуже, так как теперь все те результаты, которые ранее были подавлены в источнике (БД), будут созданы и должны быть где-то сохранены, что в конечном итоге означает живую память, выделенную для хранения, и может сделать вещи далеко хуже, чем сейчас.

Если бы я был в вашей обуви прямо сейчас, я бы сосредоточился на определении того, где происходит эта блокировка ProcessRow. Гипотеза, которую я выдвинул, заключалась в том, что эта обработка будет представлять собой запись MVC View в буфер ответа и, в свою очередь, блокируется управлением потоком TCP, в результате чего пользовательский агент не потребляет HTTP-ответ (например, Ajax-вызов завершен, но браузер не работает код завершения, чтобы потреблять ответ, потому что основной поток зацикливается на чем-то другом). Как всегда, наилучшим подходом является методическая оценка. Некоторые возможные инструменты: