为什么对于不同的项目,相同的Linq-to-SQL查询在数据库服务器上消耗更多的CPU时间

本文关键字:服务器 时间 CPU 数据库 查询 项目 于不同 为什么 Linq-to-SQL | 更新日期: 2023-09-27 18:20:50

我有一个遗留.Net 4项目(名称为"a"),它使用Linq to SQL查询数据库我有另一个.Net 4项目(名称为"B"),它具有类似但不同的代码,可以查询与"A"相同的数据库。

两个项目:

  • 是C#项目{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
  • 使用相同的程序集(版本v4.0.30119,相同的文件夹)
    • System.dll
    • System.Data.dll
    • System.Data.Linq.dll

自动生成的DataContext对每个项目都是特定的,但以相同的方式实例化:

  • 使用SQL身份验证的相同连接字符串
  • 两个DataContext都将其CommandTimeout从默认值设置为60秒
  • DataContext的所有其他配置选项都是默认选项

构建Linq查询的方式对于项目来说并不完全相同,但生成的Linq查询是相同的。生成的(T-)SQL select语句也是一样的(监控并验证数据库服务器上的SQL句柄)

数据库服务器为:

  • Microsoft SQL Server Enterprise 2005 x64(9.00.4035.00)
  • 操作系统:Microsoft Server 2003 R2 SP2 x64

如果运行,则项目"A"的查询的监控CPU时间(在数据库服务器上)急剧增加,并引发命令超时异常。(System.Data.SqlClient.SqlException:超时已过期)

另一方面,"B"的查询在秒内执行(大约3)。我能够通过再次调用具有相同参数的代码"A"来再现行为(不对代码或数据库进行更改)。"B"甚至在几秒钟内执行,同时"A"正在增加其CPU时间。

很遗憾,在一位同事重新创建指数后,我再也无法谴责这种行为了同一位同事提到,查询在"上个月"运行得很快(尽管没有代码从"上月"更改…)

我调试了这两个项目的代码——两个DataContext实例看起来都很相似。数据库服务器进程的sql句柄包含相同的sql语句。但是"A"抛出超时异常,"B"在几秒钟内执行——重复!

为什么对于项目"A",相同的Linq到SQL查询在数据库服务器上消耗的CPU时间比对于"B"要多得多

确切地说:如果查询由于重复的原因而运行"慢",那么同一个查询怎么能因为被另一个Linq-To-SQL代码调用而运行得更快呢?

会有我还不知道的副作用吗?是否有一些DataContext的实例值需要我在运行时专门查看?

顺便说一句:SQL语句通过SSMS在每次运行中使用相同的查询计划。

为了完整起见,我链接了一个示例:

  • 项目"B"的C#代码片段(两个项目的SqlRequest.GetQuery部分看起来相似)
  • SQL文件包含适当的数据库架构
  • 数据库执行计划

请记住,我不能透露完整的数据库模式、代码或我正在查询的实际数据。(SQL表除了命名的列之外还有其他列,C#代码有点复杂,因为Linq查询是有条件构建的。)

更新-在运行时获得更多见解

两个DataContext实例的一些性质:

Log = null;
Transaction = null;
CommandTimeout = 60;
Connection: System.Data.SqlClient.SqlConnection;

SqlConnection是从这样的连接字符串创建的(两种情况):

"Data Source=server;Initial Catalog=sourceDb;Persist Security Info=True;User ID=user;Password=password"

没有运行显式SqlCommands将SET选项传递到数据库会话。两者都不包含内联TVF SET选项。

为什么对于不同的项目,相同的Linq-to-SQL查询在数据库服务器上消耗更多的CPU时间

您需要在SQL Server上运行跟踪,而不是从C#端进行调试。这将显示AB在服务器上执行的所有内容。执行计划对你没有好处,因为它恰恰是一个计划。您希望查看精确的语句及其实际性能指标。

在极少数情况下,您告诉我,两个SELECT语句完全相同,但性能大不相同。我几乎可以肯定,它们在不同的事务隔离级别下运行。即使您没有显式创建任何SQL命令,单个SQL命令也是一个隐式事务。

无论出于何种原因,如果跟踪没有表明,您应该发布正在运行的命令及其度量。

注意:运行跟踪会带来一些性能开销,所以我会尽量缩短时间段,或者尽可能在非高峰时段运行。

我认为您将在"A"项目edmx文件中检查LazyLoadingEnabled="true"。

如果LazyLoadingEnabled="true":在延迟加载的情况下,相关对象(子对象)在被请求之前不会自动与其父对象一起加载。默认LINQ支持延迟加载。

IF LazyLoadingEnabled="false":在急切加载的情况下,相关对象(子对象)将与其父对象一起自动加载。要使用Eagle加载,您需要使用Include()方法。