Tôi đang gặp sự cố lớn về hiệu suất SQL khi sử dụng lệnh gọi không đồng bộ. Tôi đã tạo một trường hợp nhỏ để chứng minh vấn đề.
Tôi đã tạo cơ sở dữ liệu trên SQL Server 2016 nằm trong mạng LAN của chúng tôi (vì vậy không phải là localDB).
Trong cơ sở dữ liệu đó, tôi có một bảng WorkingCopy
với 2 cột:
Id (nvarchar(255, PK))
Value (nvarchar(max))
DDL
CREATE TABLE [dbo].[Workingcopy]
(
[Id] [nvarchar](255) NOT NULL,
[Value] [nvarchar](max) NULL,
CONSTRAINT [PK_Workingcopy]
PRIMARY KEY CLUSTERED ([Id] ASC)
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF,
IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON,
ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY] TEXTIMAGE_ON [PRIMARY]
Trong bảng đó, tôi đã chèn một bản ghi duy nhất ( id
= 'PerfUnitTest', Value
là một chuỗi 1,5mb (một zip của tập dữ liệu JSON lớn hơn)).
Bây giờ, nếu tôi thực hiện truy vấn trong SSMS:
SELECT [Value]
FROM [Workingcopy]
WHERE id = 'perfunittest'
Tôi ngay lập tức nhận được kết quả và tôi thấy trong SQL Servre Profiler rằng thời gian thực thi là khoảng 20 mili giây. Tất cả đều bình thường.
Khi thực thi truy vấn từ mã .NET (4.6) bằng cách sử dụng SqlConnection
:
// at this point, the connection is already open
var command = new SqlCommand($"SELECT Value FROM WorkingCopy WHERE Id = @Id", _connection);
command.Parameters.Add("@Id", SqlDbType.NVarChar, 255).Value = key;
string value = command.ExecuteScalar() as string;
Thời gian thực hiện việc này cũng khoảng 20-30 mili giây.
Nhưng khi thay đổi nó thành mã không đồng bộ:
string value = await command.ExecuteScalarAsync() as string;
Thời gian thực hiện đột ngột là 1800 ms ! Ngoài ra trong SQL Server Profiler, tôi thấy rằng thời lượng thực thi truy vấn là hơn một giây. Mặc dù truy vấn được thực thi được trình biên dịch báo cáo là hoàn toàn giống với phiên bản không Async.
Nhưng nó trở nên tồi tệ hơn. Nếu tôi nghịch ngợm với Kích thước gói trong chuỗi kết nối, tôi nhận được kết quả sau:
Kích thước gói 32768: [TIMING]: ExecuteScalarAsync trong SqlValueStore -> thời gian đã trôi qua: 450 ms
Kích thước gói 4096: [TIMING]: ExecuteScalarAsync trong SqlValueStore -> thời gian đã trôi qua: 3667 ms
Kích thước gói 512: [TIMING]: ExecuteScalarAsync trong SqlValueStore -> thời gian đã trôi qua: 30776 ms
30.000 ms !! Chậm hơn 1000 lần so với phiên bản không đồng bộ. Và SQL Server Profiler báo cáo rằng việc thực thi truy vấn mất hơn 10 giây. Điều đó thậm chí không giải thích được 20 giây còn lại sẽ đi đến đâu!
Sau đó, tôi đã quay trở lại phiên bản đồng bộ và cũng chơi với Kích thước gói, và mặc dù nó có ảnh hưởng một chút đến thời gian thực thi, nhưng nó không có gì ấn tượng như với phiên bản không đồng bộ.
Như một ghi chú bên, nếu nó chỉ đặt một chuỗi nhỏ (<100byte) vào giá trị, thì việc thực thi truy vấn không đồng bộ cũng nhanh như phiên bản đồng bộ (kết quả trong 1 hoặc 2 mili giây).
Tôi thực sự bối rối bởi điều này, đặc biệt là vì tôi đang sử dụng cài sẵn SqlConnection
, thậm chí không phải ORM. Ngoài ra, khi tìm kiếm xung quanh, tôi không tìm thấy gì có thể giải thích hành vi này. Bất kỳ ý tưởng?