간단한 루프로 ASYNC_NETWORK_IO가 대기하는 이유는 무엇입니까?


19

SSMS v17.9가 설치된 시스템에서 다음 T-SQL은 약 25 초가 걸립니다.

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;

ASYNC_NETWORK_IO모두에 따라 532ms의 대기 시간 이 누적 됩니다. 루프 반복 횟수가 증가하면 총 대기 시간이 늘어납니다. 확장 이벤트를 사용하면 몇 가지 예외를 제외하고 대략 43ms마다 대기가 발생한다는 것을 알 수 있습니다.sys.dm_exec_session_wait_statssys.dm_os_wait_statswait_completed

대기 테이블

또한 ASYNC_NETWORK_IO대기 직전에 발생하는 호출 스택을 얻을 수 있습니다 .

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

마지막으로, SSMS는 루프 동안 (평균 코어의 절반 정도) 놀라운 양의 CPU를 사용하는 것으로 나타났습니다. 그 시간 동안 SSMS가 무엇을하고 있는지 알 수 없습니다.

ASYNC_NETWORK_IOSSMS를 통해 실행될 때 단순한 루프로 인해 대기가 발생하는 이유는 무엇 입니까? 이 쿼리 실행에서 클라이언트로부터 얻은 유일한 출력은 "명령이 성공적으로 완료되었습니다." 메시지.

답변:


31

문서 에는 SET NOCOUNT말합니다 :

SET NOCOUNT ONDONE_IN_PROC저장 프로 시저의 각 명령문에 대해 클라이언트 로 메시지를 보내지 못하게합니다 . 설정 거래-SQL 루프를 포함하는 절차에 대한 많은 실제 데이터를 반환하지 않거나 여러 개의 문이 포함 된 저장 프로 시저의 경우 SET NOCOUNTON네트워크 트래픽이 크게 감소하기 때문에, 상당한 성능 향상을 제공 할 수 있습니다.

저장 프로 시저에서 문을 실행하지 않으므로 SQL Server는 DONE토큰 (code 0xFD)을 보내 각 SQL 문의 완료 상태를 나타냅니다. 이러한 메시지는 지연되고 네트워크 패킷이 가득 차면 비동기 적으로 전송됩니다. 클라이언트가 네트워크 패킷을 충분히 빨리 소비하지 않으면 결국 버퍼가 가득 차고 작업이 SQL Server에 대해 차단되어 ASYNC_NETWORK_IO대기를 생성합니다 .

메모 DONE토큰이 다르다 DONEINPROC(코드 0xFF문서 노트 등) :

  • DONE토큰은 변수 선언을 제외하고 SQL 일괄 처리의 각 SQL 문에 대해 반환됩니다.

  • SQL의 저장 프로 시저 내에서 문장의 실행 DONEPROCDONEINPROC토큰 대신에 사용되는 DONE토큰.

다음을 ASYNC_NETWORK_IO사용하여 대기 시간 이 크게 줄어 듭니다.

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;

sys.sp_executesql동일한 결과를 얻기 위해 사용할 수도 있습니다.

ASYNC_NETWORK_IO대기가 시작될 때 캡처 된 스택 추적 예제 :

패킷 보내기

인라인 함수에서 볼 수있는 TDS 패킷의 예 sqllang!srv_completioncode_ex<1>는 다음과 같은 13 바이트입니다.

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

어떤 디코딩 :

  • 토큰 종류 = 0xfd DONE_TOKEN
  • 상태 = 0x0001 DONE_MORE
  • CurCmd = 0x00c1 (193)
  • DoneRowCount = 0x00000001 (1)

궁극적으로 ASYNC_NETWORK_IO대기 횟수 는 클라이언트와 드라이버 및 모든 DONE메시지 에서 수행되는 작업 (있는 경우)에 따라 다릅니다 . 질문에 주어진 크기의 1/10 루프로 테스트 (5,000,000 루프 반복) SSMS가 200-300ms 대기로 약 4 초 동안 실행되는 것을 발견했습니다. sqlcmd한 자릿수 ms 대기로 2-3 초 동안 실행되었습니다. osql약 10ms의 대기 시간과 동일한 런타임.

이 테스트에서 최악의 클라이언트는 Azure Data Studio였습니다. 거의 6 시간 동안 실행되었습니다.

ADS

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.