Tại sao một vòng lặp đơn giản dẫn đến ASYNC_NETWORK_IO chờ đợi?


19

T-SQL sau mất khoảng 25 giây trên máy của tôi với 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;

Nó tích lũy 535 ms ASYNC_NETWORK_IOchờ đợi theo cả hai sys.dm_exec_session_wait_statssys.dm_os_wait_stats. Tổng thời gian chờ tăng lên khi số lần lặp lặp tăng lên. Sử dụng wait_completedsự kiện mở rộng tôi có thể thấy rằng sự chờ đợi xảy ra khoảng 43 ms với một vài ngoại lệ:

bàn chờ

Ngoài ra, tôi có thể nhận được các ngăn xếp cuộc gọi xảy ra ngay trước khi ASYNC_NETWORK_IOchờ:

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

Cuối cùng, tôi nhận thấy rằng SSMS sử dụng một lượng CPU đáng ngạc nhiên trong vòng lặp (trung bình khoảng một nửa lõi). Tôi không thể hiểu SSMS đang làm gì trong thời gian đó.

Tại sao một vòng lặp đơn giản gây ra sự ASYNC_NETWORK_IOchờ đợi khi được thực hiện thông qua SSMS? Đầu ra duy nhất mà tôi dường như nhận được từ máy khách từ việc thực hiện truy vấn này là "Các lệnh đã hoàn thành thành công". thông điệp.

Câu trả lời:


31

Các tài liệu cho SET NOCOUNTbiết:

SET NOCOUNT ONngăn việc gửi DONE_IN_PROCtin nhắn đến máy khách cho mỗi câu lệnh trong một thủ tục được lưu trữ. Đối với thủ tục lưu trữ có chứa một vài câu lệnh mà không quay trở lại nhiều dữ liệu thực tế, hoặc các thủ tục có chứa vòng Transact-SQL, thiết lập SET NOCOUNTđể ONcó thể cung cấp một tăng hiệu suất đáng kể, bởi vì lưu lượng mạng được giảm đáng kể.

Bạn không chạy các câu lệnh trong một thủ tục được lưu trữ, vì vậy SQL Server sẽ gửi DONEmã thông báo (mã 0xFD) để chỉ ra trạng thái hoàn thành của mỗi câu lệnh SQL. Các tin nhắn này được hoãn lại và được gửi không đồng bộ khi gói mạng đầy. Khi máy khách không tiêu thụ các gói mạng đủ nhanh, cuối cùng bộ đệm sẽ lấp đầy và hoạt động sẽ bị chặn đối với SQL Server, tạo ra sự ASYNC_NETWORK_IOchờ đợi.

Lưu ý DONEmã thông báo khác với DONEINPROC(mã 0xFF) như ghi chú tài liệu:

  • DONEthông báo được trả về cho mỗi câu lệnh SQL trong lô SQL ngoại trừ khai báo biến.

  • Để thực thi các câu lệnh SQL trong các thủ tục được lưu trữ DONEPROCDONEINPROCcác mã thông báo được sử dụng thay cho các DONEmã thông báo.

Bạn sẽ thấy giảm đáng kể số lần ASYNC_NETWORK_IOchờ sử dụng:

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;

Bạn cũng có thể sử dụng sys.sp_executesqlđể đạt được kết quả tương tự.

Ví dụ theo dõi ngăn xếp được bắt giữ khi ASYNC_NETWORK_IOchờ đợi bắt đầu:

gửi một gói

Một gói TDS ví dụ như được thấy trong hàm nội tuyến sqllang!srv_completioncode_ex<1>có 13 byte sau:

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

Giải mã cho:

  • Mã thông báo = 0xfd DONE_TOKEN
  • Trạng thái = 0x0001 DONE_MORE
  • CurCmd = 0x00c1 (193)
  • DoneRowCount = 0x00000001 (1)

Cuối cùng, số lượng ASYNC_NETWORK_IOchờ đợi phụ thuộc vào khách hàng và trình điều khiển, và những gì nó làm, nếu có, với tất cả các DONEtin nhắn. Thử nghiệm với vòng lặp 1/10 kích thước được đưa ra trong câu hỏi (lặp 5.000.000 vòng lặp) Tôi thấy SSMS chạy trong khoảng 4 giây với 200-300 ms chờ đợi. sqlcmdchạy trong 2-3 giây với một chữ số ms chờ đợi; osqlkhoảng thời gian chạy tương tự với khoảng 10 ms chờ đợi.

Khách hàng tồi tệ nhất cho đến nay cho thử nghiệm này là Azure Data Studio. Nó chạy được gần 6 giờ:

QUẢNG CÁO

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.