从ADO.NET和SMSS执行时,具有相同查询计划的相同查询需要大约10倍的时间

Dav*_*erg 6 c# t-sql sql-server performance ado.net

我的查询相当复杂,但我已经简化了它来解决这个问题,现在它是一个简单的JOIN,我正在SQL Server 2014数据库上运行.查询是:

SELECT * FROM SportsCars as sc INNER JOIN Cars AS c ON c.CarID = sc.CarID WHERE c.Type = 1
Run Code Online (Sandbox Code Playgroud)

当我从SMSS运行此查询并在SQL事件探查器中观察它时,执行大约需要350毫秒.当我使用Entity Framework或ADO.NET在我的应用程序中运行相同的查询时(我已尝试过两者).执行需要4500ms.

ADO.NET代码:

using (var connection = new SqlConnection(connectionString))
{
    connection.Open();
    var cmdA = new SqlCommand("SET ARITHABORT ON", connection);
    cmdA.ExecuteNonQuery();

    var query = "SELECT * FROM SportsCars as sc INNER JOIN Cars AS c ON c.CarID = sc.CarID WHERE c.Type = 1";
    var cmd = new SqlCommand(query, connection);
    cmd.ExecuteNonQuery()
}
Run Code Online (Sandbox Code Playgroud)

我做了大量的谷歌搜索,发现了这篇很棒的文章和几个StackOverflow问题(这里这里).为了使我SET ARITHABORT ON在ADO.NET中调用的两个查询的会话参数相同,它没有任何区别.这是一个直接的SQL查询,因此没有参数嗅探问题.我已经将查询和索引简化为此测试的最基本形式.服务器上没有其他任何东西在运行,并且在测试期间没有其他任何东西访问数据库.Cars或SportsCars表中没有计算列,只有INT和VARCHAR.

SportsCars表有大约170k记录和4列,Cars表有大约1.2M记录和7列.结果数据集(SportsCars of Type = 1)有大约2600条记录和11列.我在Cars表上有一个非聚集索引,在[Type]列上包含cars表的所有列.两个表都在CarID列上有一个聚簇索引.两个表中都不存在其他索引.在这两种情况下,我都以相同的数据库用户身份运行.

当我在SQL事件探查器中查看数据时,我发现两个查询都使用完全相同,非常简单的查询计划.在SQL事件探查器中,我使用的是性能事件类和ShowPlan XML统计信息配置文件,我认为这是监视和捕获实际执行计划的正确事件.两个查询的读取数相同(2596).

在ADO.NET与SMSS中,具有完全相同查询计划的两个完全相同的查询如何能够延长10倍?

Dav*_*erg 6

弄清楚了:

因为我使用的是实体框架,所以我的应用程序中的连接字符串具有MultipleActiveResultSets=True. 当我从连接字符串中删除它时,查询在 ADO.NET 和 SSMS 中具有相同的性能。

显然,此设置存在问题,导致查询在通过 WAN 连接到 SQL Server 时响应缓慢。我找到了这个链接和这个评论

MARS 使用“firehose 模式”来检索数据。Firehose 模式意味着服务器将尽可能快地生成数据。这也意味着您的客户端应用程序必须以与传入数据相同的速度接收传入数据。否则,服务器上的数据存储缓冲区将填满,处理将停止,直到这些缓冲区为空。

所以呢?您可能会问... 但是只要处理停止,SQL 服务器上的资源就会被使用并被占用。这包括工作线程、模式和数据锁、内存等。因此,您的客户端应用程序在入站结果到达时尽快使用它们是至关重要的。

我必须将此设置与实体框架一起使用,否则延迟加载会产生异常。所以我将不得不想出一些其他的解决方法。但至少我现在明白了这个问题。