从ADO.NET和SMSS执行具有相同查询计划的相同查询大约需要10倍的时间当时我的查询相当复杂,但我已经对其进行了简化以解决问题,现在它是一个简单的JOIN,并且我在SQLServer2014数据库上运行。查询是:SELECT*FROMSportsCarsasscINNERJOINCarsAScONc.CarID=sc.CarIDWHEREc.Type=1当我从SMSS运行这个查询并在SQLProfiler中观察它时,执行大约需要350毫秒.当我使用EntityFramework或ADO.NET在我的应用程序中运行相同的查询时(我都尝试过)。执行需要4500毫秒。ADO.NET代码:使用(varconnection=newSqlConnection(connectionString)){connection.Open();varcmdA=newSqlCommand("SETARITHABORTON",connection);cmdA.ExecuteNonQuery();varquery="SELECT*FROMSportsCarsasscINNERJOINCarsAScONc.CarID=sc.CarIDWHEREc.Type=1";varcmd=newSqlCommand(查询,连接);cmd.ExecuteNonQuery()}我做了很多谷歌搜索,找到了这篇很棒的文章和几个StackOverflow问题(这里和这里)。为了使两个查询的会话参数相同,我在ADO.NET中调用了SETARITHABORTON,它没有任何区别。这是一个直接的SQL查询,因此不存在参数嗅探问题。我已将查询和索引简化为此测试的最基本形式。服务器上没有其他任何东西在运行,在测试期间也没有其他东西在访问数据库。Cars或SportsCars表中没有计算列,只有INT和VARCHAR。SportsCars表有大约170k条记录和4列,Cars表有大约1.2M条记录和7列。生成的数据集(SportsCarsofType=1)有大约2600条记录和11列。我在Cars表上有一个非聚集索引,其中包含[Type]列上cars表的所有列。两个表在CarID列上都有一个聚集索引。两个表上都不存在其他索引。在这两种情况下,我都以相同的数据库用户身份运行。当我在SQLProfiler中查看数据时,我发现这两个查询使用完全相同且非常简单的查询计划。在SQLProfiler中,我使用了性能事件类和ShowPlanXML统计配置文件,我认为这是监视和捕获实际执行计划的正确事件。两个查询的读取次数相同(2596)。在ADO.NET和SMSS中,具有完全相同查询计划的两个相同查询如何能长10倍?弄清楚了:因为我使用的是EntityFramework,所以我应用程序中的连接字符串具有MultipleActiveResultSets=True。当我从连接字符串中删除它时,查询在ADO.NET和SSMS中具有相同的性能。显然,此设置存在问题,导致在通过WAN连接到SQLServer时查询响应缓慢。我找到了这个链接和这个评论:MARS使用“firehose模式”来检索数据。Firehose模式意味着服务器将尽快生成数据。这也意味着您的客户端应用程序必须以与其相同的速度接收入站数据。否则,服务器上的数据存储缓冲区将填满并且处理将停止,直到这些缓冲区为空。所以呢?您可能会问...但是只要处理停止,就会使用并占用SQL服务器上的资源。这包括工作线程、模式和数据锁、内存等。因此,客户端应用程序在入站结果到达后立即使用它们至关重要。我必须在EntityFramework中使用这个设置,否则延迟加载会产生异常。所以我将不得不想出一些其他的解决方法。但至少我现在明白了这个问题。在ADO.NET和SMSS中,具有完全相同查询计划的两个相同查询如何能长10倍?首先,我们需要明确什么是查询和查询计划中的“相同”。假设问题顶部的查询是复制粘贴的,这与通过ADO.NET提交的查询不同。要使两个查询相同,它们需要逐字节相同,包括所有空格、大写、标点符号、注释等。显示的两个查询肯定非常相似。他们甚至可能共享相同的执行计划。但是那些人是如何判断“相同”的呢?两种情况下的XML是否相同?或者它只是在查看计划时在SSMS中以图形方式显示的内容?如果基于图形表示确定它们是相同的,这有时会产生误导。需要检查XML本身。即使两个查询计划具有相同的查询哈希,查询计划的某些部分仍然(有时)是可变的,并且更改不会更改计划哈希。一个例子是表达式的评估。有时会计算它们并将其结果作为常量嵌入到计划中。有时它们是在每次执行开始时计算的,在该特定执行中存储和重用,但不可用于任何后续执行。SSMS和ADO.NET之间的一个区别是各自的默认会话属性。我想我几年前看到一张图显示ADO/OLEDB/SQLNCLI的默认值,但是没找到。无论哪种方式,都不需要猜测,因为可以使用SESSIONPROPERTY函数发现它。只需在C#代码中运行此查询而不是当前的SELECT并在调试中检查结果或打印它或其他任何东西。无论哪种方式,运行这样的东西:确保在链接的MSDN页面中记录所有设置。现在,在SSMS中,转到“查询”菜单,选择“查询选项...”,然后转到“执行”|“美标”。从C#代码返回的设置需要与SSMS中显示的设置相匹配。任何不同的设置都需要在ADO.NET查询字符串的开头添加类似这样的内容:SETANSI_NULLSON;{restofquery}现在,如果您想消除DataTable加载的可能性,只需将该行替换为Toreplace:varcars=newDataTable();cars.Load(阅读器);与:while(reader.Read());最后,为什么不把查询放到一个存储过程中呢?通常最重要的会话设置(即ANSI_NULLS等)与proc定义一起存储,因此无论您是从SSMS还是从ADO.NET执行EXEC,它们都应该工作相同(同样,我们在这里不处理任何参数)。以上是C#学习教程:从ADO.NET和SMSS执行相同的查询,相同的查询计划,耗时大约10倍请注意,本文收集自网络,不代表立场。如涉及侵权,请点击右侧联系管理员删除。如需转载请注明出处:
