Perché un semplice loop risulta in ASYNC_NETWORK_IO in attesa?


19

Il seguente T-SQL impiega circa 25 secondi sul mio computer con 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;

Accumula 532 ms di ASYNC_NETWORK_IOattese secondo entrambi sys.dm_exec_session_wait_statse sys.dm_os_wait_stats. Il tempo di attesa totale aumenta all'aumentare del numero di iterazioni di loop. Utilizzando l' wait_completedevento esteso posso vedere che l'attesa si verifica all'incirca ogni 43 ms con alcune eccezioni:

aspetta il tavolo

Inoltre, posso ottenere le pile di chiamate che si verificano subito prima ASYNC_NETWORK_IOdell'attesa:

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

Infine, ho notato che SSMS utilizza una quantità sorprendente di CPU durante il ciclo (circa mezzo core in media). Non riesco a capire cosa stia facendo SSMS in quel periodo.

Perché una semplice causa loop ASYNC_NETWORK_IOattende quando viene eseguita tramite SSMS? L'unico output che mi sembra di ottenere dal client da questa esecuzione della query sono i "Comandi completati correttamente". Messaggio.

Risposte:


31

La documentazione per SET NOCOUNTdice:

SET NOCOUNT ONimpedisce l'invio di DONE_IN_PROCmessaggi al client per ogni istruzione in una procedura memorizzata. Per le stored procedure che contengono diverse istruzioni che non restituiscono molti dati effettivi o per le procedure che contengono loop Transact-SQL, impostazioneSET NOCOUNT su ONpuò fornire un significativo incremento delle prestazioni, poiché il traffico di rete è notevolmente ridotto.

Non si eseguono le istruzioni in una stored procedure, quindi SQL Server invia DONEtoken (codice0xFD ) per indicare lo stato di completamento di ciascuna istruzione SQL. Questi messaggi vengono rinviati e inviati in modo asincrono quando il pacchetto di rete è pieno. Quando il client non consuma abbastanza rapidamente i pacchetti di rete, alla fine i buffer si riempiono e l'operazione diventa bloccante per SQL Server, generando le ASYNC_NETWORK_IOattese.

Nota che i DONEtoken sono diversi da DONEINPROC(codice 0xFF) come le note della documentazione:

  • Viene DONErestituito un token per ogni istruzione SQL nel batch SQL ad eccezione delle dichiarazioni delle variabili.

  • Per l'esecuzione di istruzioni SQL all'interno stored procedure, DONEPROCe DONEINPROCtoken sono utilizzati al posto di DONEgettoni.

Vedrai una drastica riduzione delle ASYNC_NETWORK_IOattese usando:

CREATE PROCEDURE #P AS
SET NOCOUNT ON;

DECLARE
    @outer_loop integer = 0,
    @big_string_for_u varchar(8000);


WHILE @outer_loop < 5000000
BEGIN
    SET @big_string_for_u = 'ZZZZZZZZZZ';
    SET @outer_loop = @outer_loop + 1;
END;
GO
EXECUTE dbo.#P;

Puoi anche usare sys.sp_executesql per ottenere lo stesso risultato.

Traccia dello stack di esempio catturata appena ASYNC_NETWORK_IOinizia l'attesa:

invio di un pacchetto

Un pacchetto TDS di esempio visto nella funzione inline sqllang!srv_completioncode_ex<1>aveva i seguenti 13 byte:

fd 01 00 c1 00 01 00 00 00 00 00 00 00          

Che decodifica per:

  • TokenType = 0xfd DONE_TOKEN
  • Stato = 0x0001 DONE_MORE
  • CurCmd = 0x00c1 (193)
  • DoneRowCount = 0x00000001 (1)

In definitiva, il numero di ASYNC_NETWORK_IOattese dipende dal client e dal driver e da cosa fa, se non altro, con tutti i DONEmessaggi. Testando con un loop 1/10 della dimensione indicata nella domanda (5.000.000 di iterazioni di loop) ho scoperto che SSMS ha funzionato per circa 4 secondi con 200-300 ms di attese. sqlcmdha funzionato per 2-3 secondi con una ms in attesa di una cifra; osqlnello stesso tempo di esecuzione con circa 10 ms di attese.

Il peggior client di gran lunga per questo test è stato Azure Data Studio. Ha funzionato per quasi 6 ore:

ANNUNCI

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.