У мене виникають основні проблеми з продуктивністю SQL під час використання асинхронних викликів. Я створив невеликий випадок, щоб продемонструвати проблему.
Я створив базу даних на SQL Server 2016, яка знаходиться в нашій локальній мережі (отже, не в localDB).
У цій базі даних у мене є таблиця WorkingCopy
з 2 стовпцями:
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]
У цю таблицю я вставив один запис ( id
= 'PerfUnitTest', Value
це рядок розміром 1,5 Мб (zip більшого набору даних JSON)).
Тепер, якщо я виконую запит у SSMS:
SELECT [Value]
FROM [Workingcopy]
WHERE id = 'perfunittest'
Я негайно отримую результат, і я бачу в SQL Servre Profiler, що час виконання становив близько 20 мілісекунд. Все нормально.
При виконанні запиту з коду .NET (4.6) за допомогою простого 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;
Час виконання для цього також становить близько 20-30 мілісекунд.
Але при зміні його на асинхронний код:
string value = await command.ExecuteScalarAsync() as string;
Час виконання раптово 1800 мс ! Також у програмі SQL Server Profiler я бачу, що тривалість виконання запиту перевищує секунду. Хоча виконаний запит, про який повідомляє профайлер, точно такий самий, як і версія, що не є асинхронною.
Але стає гірше. Якщо я пограю з розміром пакета в рядку підключення, я отримаю такі результати:
Розмір пакета 32768: [TIMING]: ExecuteScalarAsync in SqlValueStore -> минулий час: 450 мс
Розмір пакета 4096: [TIMING]: ExecuteScalarAsync in SqlValueStore -> минулий час: 3667 мс
Розмір пакета 512: [TIMING]: ExecuteScalarAsync in SqlValueStore -> минулий час: 30776 мс
30000 мс !! Це майже в 1000 разів повільніше, ніж несинхронна версія. А SQL Server Profiler повідомляє, що виконання запиту зайняло більше 10 секунд. Це навіть не пояснює, куди пішли інші 20 секунд!
Потім я повернувся до версії синхронізації, а також погрався з розміром пакета, і хоча це мало вплинуло на час виконання, це ніде не було таким драматичним, як у асинхронній версії.
Як сиденот, якщо він додає у значення лише невеликий рядок (<100 байт), виконання асинхронного запиту відбувається так само швидко, як і версія синхронізації (результат в 1 або 2 мс).
Мене це справді бентежить, тим більше, що я використовую вбудований SqlConnection
, навіть не ORM. Також під час пошуку навколо я не знайшов нічого, що могло б пояснити таку поведінку. Будь-які ідеї?