O seguinte T-SQL leva cerca de 25 segundos na minha máquina com SSMS v17.9:
DECLARE @outer_loop INT = 0,
@big_string_for_u VARCHAR(8000);
SET NOCOUNT ON;
WHILE @outer_loop < 50000000
BEGIN
SET @big_string_for_u = 'ZZZZZZZZZZ';
SET @outer_loop = @outer_loop + 1;
END;
Acumula 532 ms de ASYNC_NETWORK_IO
esperas de acordo com sys.dm_exec_session_wait_stats
e sys.dm_os_wait_stats
. O tempo total de espera aumenta à medida que o número de iterações de loop aumenta. Usando o wait_completed
evento estendido, posso ver que a espera acontece aproximadamente a cada 43 ms, com algumas exceções:
Além disso, posso obter as pilhas de chamadas que ocorrem logo antes da ASYNC_NETWORK_IO
espera:
sqldk.dll!SOS_DispatcherBase::GetTrack+0x7f6c
sqldk.dll!SOS_Scheduler::PromotePendingTask+0x204
sqldk.dll!SOS_Task::PostWait+0x5f
sqldk.dll!SOS_Scheduler::Suspend+0xb15
sqllang.dll!CSECCNGProvider::GetBCryptHandleFromAlgID+0xf6af
sqllang.dll!CSECCNGProvider::GetBCryptHandleFromAlgID+0xf44c
sqllang.dll!SNIPacketRelease+0xd63
sqllang.dll!SNIPacketRelease+0x2097
sqllang.dll!SNIPacketRelease+0x1f99
sqllang.dll!SNIPacketRelease+0x18fe
sqllang.dll!CAutoExecuteAsContext::Restore+0x52d
sqllang.dll!CSQLSource::Execute+0x151b
sqllang.dll!CSQLSource::Execute+0xe13
sqllang.dll!CSQLSource::Execute+0x474
sqllang.dll!SNIPacketRelease+0x165d
sqllang.dll!CValOdsRow::CValOdsRow+0xa92
sqllang.dll!CValOdsRow::CValOdsRow+0x883
sqldk.dll!ClockHand::Statistic::RecordClockHandStats+0x15d
sqldk.dll!ClockHand::Statistic::RecordClockHandStats+0x638
sqldk.dll!ClockHand::Statistic::RecordClockHandStats+0x2ad
sqldk.dll!SystemThread::MakeMiniSOSThread+0xdf8
sqldk.dll!SystemThread::MakeMiniSOSThread+0xf00
sqldk.dll!SystemThread::MakeMiniSOSThread+0x667
sqldk.dll!SystemThread::MakeMiniSOSThread+0xbb9
Finalmente, notei que o SSMS usa uma quantidade surpreendente de CPU durante o loop (cerca de meio núcleo em média). Não consigo descobrir o que o SSMS está fazendo durante esse período.
Por que um loop simples causa ASYNC_NETWORK_IO
esperas quando executado por meio do SSMS? A única saída que pareço obter do cliente dessa execução de consulta é "Comandos concluídos com êxito". mensagem.
A documentação para
SET NOCOUNT
diz:Você não está executando as instruções em um procedimento armazenado, portanto, o SQL Server envia
DONE
tokens (código0xFD
) para indicar o status de conclusão de cada instrução SQL. Essas mensagens são adiadas e enviadas de forma assíncrona quando o pacote de rede está cheio. Quando o cliente não consome pacotes de rede com rapidez suficiente, eventualmente os buffers ficam cheios e a operação torna-se bloqueante para o SQL Server, gerando asASYNC_NETWORK_IO
esperas.Observe que os
DONE
tokens são diferentes deDONEINPROC
(code0xFF
) conforme a documentação observa:Você verá uma redução drástica nas
ASYNC_NETWORK_IO
esperas usando:Você também pode usar
sys.sp_executesql
para obter o mesmo resultado.Exemplo de rastreamento de pilha capturado assim que uma
ASYNC_NETWORK_IO
espera começa:Um exemplo de pacote TDS visto na função inline
sqllang!srv_completioncode_ex<1>
tinha os seguintes 13 bytes:Que decodifica para:
DONE_TOKEN
DONE_MORE
Em última análise, o número de
ASYNC_NETWORK_IO
esperas depende do cliente e do driver e do que ele faz, se houver, com todas asDONE
mensagens. Testando com um loop 1/10 do tamanho fornecido na pergunta (5.000.000 iterações de loop), descobri que o SSMS foi executado por cerca de 4 segundos com 200-300 ms de espera.sqlcmd
funcionou por 2-3 segundos com esperas de ms de um dígito;osql
em torno do mesmo tempo de execução com cerca de 10 ms de espera.O pior cliente para este teste foi o Azure Data Studio. Ele funcionou por quase 6 horas: