Чому простий цикл призводить до появи ASYNC_NETWORK_IO?


19

Наступний T-SQL займає близько 25 секунд на моїй машині з 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;

Він накопичує 532 мс ASYNC_NETWORK_IOочікування відповідно до sys.dm_exec_session_wait_statsі sys.dm_os_wait_stats. Загальний час очікування збільшується зі збільшенням кількості ітерацій циклу. Використовуючи wait_completedрозгорнуту подію, я бачу, що очікування відбувається приблизно кожні 43 мс за кількома винятками:

стіл очікування

Крім того, я можу отримати стеки викликів, які виникають безпосередньо перед 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 використовує дивовижну кількість процесора (в середньому близько пів'ядра). Я не можу зрозуміти, що робить SSMS за цей час.

Чому простий цикл викликає ASYNC_NETWORK_IOочікування при виконанні через SSMS? Єдиний висновок, який, як мені здається, отримує від клієнта від виконання цього запиту - це "Команди успішно виконані". повідомлення.

Відповіді:


31

Документація для SET NOCOUNTговорить:

SET NOCOUNT ONзапобігає надсиланню DONE_IN_PROCповідомлень клієнтові для кожної заяви у збереженій процедурі. Для збережених процедур , які містять кілька заяв , які не повертають багато фактичних даних, а також для процедур , які містять цикли Transact-SQL, налаштування , SET NOCOUNTщоб ONможе забезпечити значний приріст продуктивності, оскільки мережевий трафік значно знижуються.

Ви не запускаєте оператори у збереженій процедурі, тому SQL Server надсилає DONEмаркери (код 0xFD), щоб вказати стан завершення кожного оператора SQL. Ці повідомлення відкладаються та надсилаються асинхронно, коли мережевий пакет заповнений. Коли клієнт не споживає мережеві пакети досить швидко, з часом буфери заповнюються, і операція блокується для SQL Server, генеруючи ASYNC_NETWORK_IOочікування.

Зверніть увагу, що DONEжетони відрізняються від DONEINPROC(коду 0xFF), як зазначено в документації:

  • DONEМаркер повертається для кожного оператора SQL в SQL партії , за винятком оголошення змінних.

  • Для виконання операторів SQL в межах збережених процедур, DONEPROCа DONEINPROCлексеми використовуються замість 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          

Який декодує:

  • TokenType = 0xfd DONE_TOKEN
  • Статус = 0x0001 DONE_MORE
  • CurCmd = 0x00c1 (193)
  • DoneRowCount = 0x00000001 (1)

Зрештою, кількість ASYNC_NETWORK_IOочікувань залежить від клієнта та водія, і що він робить, якщо що, з усіма DONEповідомленнями. Тестуючи цикл на 1/10 розміру, вказаний у запитанні (5 000 000 ітерацій циклу), я виявив, що SSMS працює близько 4 секунд із 200-300 мс очікування. sqlcmdбігав 2-3 секунди з однозначним повідомленням мс чекає; osqlприблизно той самий час роботи з приблизно 10 мс очікування.

Найгіршим клієнтом на цей тест стала Azure Data Studio. Він пробіг майже 6 годин:

ADS

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.