Saya mengalami masalah kinerja SQL yang besar saat menggunakan panggilan async. Saya telah membuat kasus kecil untuk mendemonstrasikan masalahnya.
Saya telah membuat database di SQL Server 2016 yang berada di LAN kami (jadi bukan localDB).
Di database itu, saya memiliki tabel WorkingCopy
dengan 2 kolom:
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]
Dalam tabel itu, saya telah memasukkan satu catatan ( id
= 'PerfUnitTest', Value
adalah string 1,5mb (zip dari dataset JSON yang lebih besar)).
Sekarang, jika saya menjalankan kueri di SSMS:
SELECT [Value]
FROM [Workingcopy]
WHERE id = 'perfunittest'
Saya langsung mendapatkan hasilnya, dan saya melihat di SQL Servre Profiler bahwa waktu eksekusi sekitar 20 milidetik. Semuanya normal.
Saat menjalankan kueri dari kode .NET (4.6) menggunakan polos 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;
Waktu eksekusi untuk ini juga sekitar 20-30 milidetik.
Tetapi ketika mengubahnya menjadi kode async:
string value = await command.ExecuteScalarAsync() as string;
Waktu eksekusi tiba-tiba menjadi 1800 ms ! Juga di SQL Server Profiler, saya melihat bahwa durasi eksekusi kueri lebih dari satu detik. Meskipun kueri yang dieksekusi yang dilaporkan oleh profiler sama persis dengan versi non-Async.
Tapi itu lebih buruk. Jika saya bermain-main dengan Ukuran Paket dalam string koneksi, saya mendapatkan hasil sebagai berikut:
Ukuran paket 32768: [WAKTU]: ExecuteScalarAsync di SqlValueStore -> waktu yang berlalu: 450 ms
Ukuran Paket 4096: [WAKTU]: ExecuteScalarAsync di SqlValueStore -> waktu yang berlalu: 3667 md
Ukuran paket 512: [WAKTU]: ExecuteScalarAsync di SqlValueStore -> waktu yang berlalu: 30776 md
30.000 md !! Itu lebih dari 1000x lebih lambat dari versi non-asinkron. Dan SQL Server Profiler melaporkan bahwa eksekusi kueri memakan waktu lebih dari 10 detik. Itu bahkan tidak menjelaskan ke mana 20 detik lainnya pergi!
Kemudian saya beralih kembali ke versi sinkronisasi dan juga bermain-main dengan Ukuran Paket, dan meskipun itu berdampak sedikit pada waktu eksekusi, itu tidak sedramatis dengan versi asinkron.
Sebagai sidenote, jika ia hanya menempatkan string kecil (<100bytes) ke dalam nilai, eksekusi kueri asinkron secepat versi sinkronisasi (menghasilkan 1 atau 2 md).
Saya benar-benar bingung dengan ini, terutama karena saya menggunakan bawaan SqlConnection
, bahkan bukan ORM. Juga ketika mencari-cari, saya tidak menemukan apa pun yang dapat menjelaskan perilaku ini. Ada ide?