Linq 存储过程超时但 SSMS 快速
Posted
技术标签:
【中文标题】Linq 存储过程超时但 SSMS 快速【英文标题】:Linq Stored Procedure Timeout but SSMS Quick 【发布时间】:2013-02-13 16:12:05 【问题描述】:我有一个使用 LinqToSQL 调用的存储过程。我根本没有做任何特别的事情,例如
MyDataContext db = new MyDataContext()
var results = db.storedProcedure(param1, param2, param3)
// Do stuff
当我使用完全相同的参数运行存储过程时,我会在 2 到 6 秒之间得到结果。该数据库是一个远程数据库。
但是,当我运行存储过程时(调试后......)需要 275 秒!在正常情况下,这会产生以下异常:
[Win32Exception (0x80004005): 等待操作超时]
[SqlException (0x80131904): 超时。在操作完成之前超时时间已过或服务器没有响应。] System.Data.SqlClient.SqlConnection.OnError(SqlException异常,布尔breakConnection,Action
1 wrapCloseInAction) +1753346 System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection, Action
1 wrapCloseInAction)+5295154 System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning(TdsParserStateObject stateObj,布尔调用者HasConnectionLock,布尔异步关闭)+242 System.Data.SqlClient.TdsParser.TryRun(RunBehavior runBehavior, SqlCommand cmdHandler, SqlDataReader dataStream, BulkCopySimpleResultSet bulkCopyHandler, TdsParserStateObject stateObj, Boolean& dataReady) +1682 System.Data.SqlClient.SqlDataReader.TryConsumeMetaData() +59 System.Data.SqlClient.SqlDataReader.get_MetaData() +90 System.Data.SqlClient.SqlCommand.FinishExecuteReader(SqlDataReader ds,RunBehavior runBehavior,字符串 resetOptionsString)+365 System.Data.SqlClient.SqlCommand.RunExecuteReaderTds(CommandBehavior cmdBehavior,RunBehavior runBehavior,Boolean returnStream,Boolean async,Int32 超时,Task& 任务,Boolean asyncWrite)+1325 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior,RunBehavior runBehavior,Boolean returnStream,String 方法,TaskCompletionSource`1 完成,Int32 超时,Task& 任务,Boolean asyncWrite)+175 System.Data.SqlClient.SqlCommand.RunExecuteReader(CommandBehavior cmdBehavior, RunBehavior runBehavior, Boolean returnStream, String method) +53 System.Data.SqlClient.SqlCommand.ExecuteReader(CommandBehavior 行为,字符串方法)+134 System.Data.SqlClient.SqlCommand.ExecuteDbDataReader(CommandBehavior 行为)+41 System.Data.Common.DbCommand.ExecuteReader() +12 System.Data.Linq.SqlClient.SqlProvider.Execute(表达式查询,QueryInfo queryInfo,IObjectReaderFactory 工厂,Object[] parentArgs,Object[] userArgs,ICompiledSubQuery[] subQueries,Object lastResult)+1306 System.Data.Linq.SqlClient.SqlProvider.ExecuteAll(表达式查询,QueryInfo[] queryInfos,IObjectReaderFactory factory,Object[] userArguments,ICompiledSubQuery[] subQueries)+118 System.Data.Linq.SqlClient.SqlProvider.System.Data.Linq .Provider.IProvider.Execute(表达式查询)+342 System.Data.Linq.DataContext.ExecuteMethodCall(Object instance, MethodInfo methodInfo, Object[] parameters) +83
所有其他存储过程都以相同的方式调用,但没有一个有这个问题。远程 DB Admin 说他可以在超时发生之前看到调用开始和结束,所以这似乎与 Linq 接收数据后的步骤有关。
以前有没有人遇到过这种情况以及如何解决它?
我已尝试从 dmbl 文件中删除 SP 并重新添加它。它注意到其中一个值从十进制变为双精度,但除此之外都一样。
和往常一样,昨天工作正常!
提前致谢。
【问题讨论】:
【参考方案1】:好的,我终于找到了这个问题的真正答案。 SSMS 通常使用 ARITHABORT ON 并且代码通常使用 ARITHABORT OFF - 这基本上是一个选项,用于在代码中的数学行有错误时如何处理发生的情况 - 例如除以零。
不过,这里的主要问题是两种方法都有不同的执行计划 - 这就是为什么相同的事情在网站上可能(随机)比在 SSMS 中花费更长的时间。
执行计划是根据第一次使用时的估计来编译的,因此您随机发现执行计划以一种适合您的第一次查询的糟糕方式缓存,但对于后续查询来说却很糟糕。这就是这里发生的事情,这也是为什么它突然又开始工作的原因 - 在存储过程更改后创建了一个新的查询计划。
最后我们在存储过程中使用了 WITH RECOMPILE - 因此没有有效地重用执行计划,但无论如何我们没有注意到任何差异,此后问题就没有发生过。
【讨论】:
【参考方案2】:造成这个问题的原因是一个依赖于 Linq.Table.Count() 的循环。在开发环境中,底层查询几乎是即时的,但在生产环境中需要几秒钟。连接 SQL Profiler 后发现,每次循环迭代都会执行查询,因此这“几秒钟”开始累加,最终超时。
我的解决方案是将 Count() 结果分配给一个局部变量,并在循环中使用它,这样 Count() 就不会在每次迭代时重新执行查询。我想如果其他人依赖内置的 Linq 聚合函数会重新执行慢速查询,他们会遇到这个问题。
【讨论】:
以上是关于Linq 存储过程超时但 SSMS 快速的主要内容,如果未能解决你的问题,请参考以下文章
ExecuteDataSet 超时发生在非常快速的存储过程上