Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/csharp/285.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C# 使用SqlCommand异步方法处理大数据时,性能非常糟糕_C#_.net_Sql Server_Asynchronous_Async Await - Fatal编程技术网

C# 使用SqlCommand异步方法处理大数据时,性能非常糟糕

C# 使用SqlCommand异步方法处理大数据时,性能非常糟糕,c#,.net,sql-server,asynchronous,async-await,C#,.net,Sql Server,Asynchronous,Async Await,我在使用异步调用时遇到了严重的SQL性能问题。我创建了一个小案例来演示这个问题 我已经在SQL Server 2016上创建了一个数据库,它位于我们的LAN中(因此不是localDB) 在该数据库中,我有一个带有两列的表WorkingCopy: Id (nvarchar(255, PK)) Value (nvarchar(max)) DDL CREATE TABLE [dbo].[Workingcopy] ( [Id] [nvarchar](255) NOT NULL, [V

我在使用异步调用时遇到了严重的SQL性能问题。我创建了一个小案例来演示这个问题

我已经在SQL Server 2016上创建了一个数据库,它位于我们的LAN中(因此不是localDB)

在该数据库中,我有一个带有两列的表
WorkingCopy

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
='perfunitest',
Value
是一个1.5mb的字符串(一个较大JSON数据集的zip)

现在,如果我在SSMS中执行查询:

SELECT [Value] 
FROM [Workingcopy] 
WHERE id = 'perfunittest'
我立即得到了结果,我在SQLServre分析器中看到执行时间约为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 ms!同样在SQL Server Profiler中,我看到查询执行持续时间超过一秒。尽管探查器报告的已执行查询与非异步版本完全相同

但情况变得更糟了。如果我在连接字符串中处理数据包大小,我会得到以下结果:

数据包大小32768:[计时]:SqlValueStore中的ExecuteScalarAsync-> 运行时间:450毫秒

数据包大小4096:[计时]:SqlValueStore中的ExecuteScalarAsync-> 运行时间:3667毫秒

数据包大小512:[计时]:SqlValueStore中的ExecuteScalarAsync-> 运行时间:30776毫秒

30000毫秒!!这比非异步版本慢1000倍以上。SQL Server Profiler报告查询执行耗时超过10秒。这甚至不能解释剩下的20秒到哪里去了

然后我又切换回同步版本,并对数据包大小进行了调整,尽管它确实对执行时间产生了一些影响,但它没有异步版本那么引人注目

作为旁注,如果只在值中放入一个小字符串(<100字节),则异步查询的执行速度与同步版本一样快(结果为1或2毫秒)


我对此感到非常困惑,尤其是因为我使用的是内置的
SqlConnection
,甚至不是ORM。另外,在四处搜索时,我发现没有任何东西可以解释这种行为。有什么想法吗?

在没有明显负载的系统上,异步调用的开销稍大一些。尽管I/O操作本身是异步的,但阻塞可以比线程池任务切换更快

管理费是多少?让我们看看你的计时数字。30毫秒用于阻塞调用,450毫秒用于异步调用。32kib数据包大小意味着您需要大约50个单独的I/O操作。这意味着我们在每个数据包上大约有8ms的开销,这与您在不同数据包大小上的测量结果相当吻合。这听起来不像是异步带来的开销,尽管异步版本比同步版本需要做更多的工作。听起来,同步版本是(简化的)1个请求->50个响应,而异步版本最终是1个请求->1个响应->1个请求->1个响应->,一次又一次地付出了成本

深入
ExecuteReader
ExecuteReaderAsync
一样有效。下一个操作是
Read
,后面是
GetFieldValue
——这里发生了一件有趣的事情。如果两者中的任何一个是异步的,则整个操作都很慢。因此,一旦你开始使事情真正异步,肯定会发生一些非常不同的事情-读取
会很快,然后异步
GetFieldValueAsync
会很慢,或者你可以从慢速
ReadAsync
开始,然后
GetFieldValueAsync
GetFieldValueAsync
都很快。从流中第一次异步读取的速度很慢,速度完全取决于整行的大小。如果我添加更多相同大小的行,那么读取每一行所需的时间与仅读取一行所需的时间相同,因此很明显,数据仍在逐行传输-似乎更喜欢在启动任何异步读取后立即读取整行。如果我异步读取第一行,而同步读取第二行,则正在读取的第二行将再次变快

因此,我们可以看到问题在于单个行和/或列的大小过大。不管你总共有多少数据,异步读取一百万小行的速度和同步读取一样快。但是,如果只添加一个太大而无法放入单个数据包中的字段,那么异步读取该数据会产生神秘的成本——就像每个数据包都需要一个单独的请求数据包一样,服务器不能一次发送所有数据。使用
CommandBehavior.SequentialAccess确实可以像预期的那样提高性能,但是同步和异步之间仍然存在巨大的差距

我得到的最好的表现是把整个事情做好。这意味着使用
CommandBehavior.SequentialAccess
,以及显式地流式传输数据:

using (var reader = await cmd.ExecuteReaderAsync(CommandBehavior.SequentialAccess))
{
  while (await reader.ReadAsync())
  {
    var data = await reader.GetTextReader(0).ReadToEndAsync();
  }
}
这样一来,同步和异步之间的差异就变得难以衡量,改变数据包大小不再像以前那样带来荒谬的开销


如果您希望在边缘情况下获得良好的性能,请确保使用可用的最佳工具-在这种情况下,流式传输大列数据,而不是依赖于帮助程序,如
ExecuteScalar
GetFieldValue

@hcd 1.5 MB?????您会问,为什么随着数据包大小的减小,检索速度会变慢?尤其是当您对blob使用错误的查询时?@PanagiotisKanavos,它只是代表OP玩而已。实际的问题是,为什么与使用相同p的sync相比,async要慢得多