【发布时间】:2018-03-18 04:05:20
【问题描述】:
我在使用异步调用时遇到了主要的 SQL 性能问题。我创建了一个小案例来演示这个问题。
我在位于我们 LAN 中的 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.5mb 的字符串(一个较大的 JSON 数据集的 zip))。
现在,如果我在 SSMS 中执行查询:
SELECT [Value]
FROM [Workingcopy]
WHERE id = 'perfunittest'
我立即得到结果,我在 SQL Servre Profiler 中看到执行时间约为 20 毫秒。一切正常。
使用纯 SqlConnection 从 .NET (4.6) 代码执行查询时:
// 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 中,我看到查询执行持续时间超过一秒。虽然 profiler 报告的执行查询与非 Async 版本完全相同。
但情况会变得更糟。如果我在连接字符串中使用 Packet Size,我会得到以下结果:
数据包大小 32768 : [TIMING]: SqlValueStore 中的 ExecuteScalarAsync -> 经过时间:450 毫秒
数据包大小 4096 : [TIMING]: SqlValueStore 中的 ExecuteScalarAsync -> 经过时间:3667 毫秒
数据包大小 512 : [TIMING]: SqlValueStore 中的 ExecuteScalarAsync -> 经过时间:30776 毫秒
30,000 毫秒!!这比非异步版本慢 1000 倍以上。 SQL Server Profiler 报告查询执行耗时超过 10 秒。这甚至无法解释其他 20 秒的去向!
然后我切换回了同步版本,并且还使用了数据包大小,虽然它确实影响了一点执行时间,但它远没有异步版本那么引人注目。
作为旁注,如果它只将一个小字符串(
我对此感到非常困惑,尤其是因为我使用的是内置的SqlConnection,甚至不是 ORM。同样在四处搜索时,我没有发现任何可以解释这种行为的东西。有什么想法吗?
【问题讨论】:
-
@hcd 1.5 MB ?????你问为什么随着数据包大小的减小,检索会变得变慢?尤其是当您对 BLOB 使用 错误 查询时?
-
@PanagiotisKanavos 那只是代表 OP 在玩。实际的问题是为什么 async 与 same 包大小的同步相比要慢得多。
-
检查 Modifying Large-Value (max) Data in ADO.NET 以获取检索 CLOB 和 BLOB 的正确方法。 而不是尝试将它们作为一个大值来读取,而是使用
GetSqlChars或GetSqlBinary以流方式检索它们。还可以考虑将它们存储为 FILESTREAM 数据 - 没有理由在表的数据页中保存 1.5MB 的数据 -
@PanagiotisKanavos 这不正确。 OP 写入同步:20-30 毫秒,与其他所有内容异步 1800 毫秒。改变数据包大小的效果是完全清楚和预期的。
-
@hcd 似乎您可以删除有关您尝试更改包裹大小的部分,因为它似乎与问题无关,并导致一些评论者混淆。
标签: c# .net sql-server asynchronous async-await