同一 SPROC 的每次迭代执行时间变慢
Posted
技术标签:
【中文标题】同一 SPROC 的每次迭代执行时间变慢【英文标题】:Execution Time Slower with each Iteration of the same SPROC 【发布时间】:2012-04-11 20:15:04 【问题描述】:从 C# .Net 应用程序通过网络运行相同存储过程会随着每次后续执行而逐渐变慢。它似乎花费了上一次执行的 两倍 时间(达到最大值;请继续阅读)。执行时间逐渐变慢,直到发生 2 个场景中的一个,此时 SPROC 的第一次执行再次“快速”。
-
如果
SqlConnection
在所有测试期间打开并保持打开状态,
在运行 任何其他 SPROC 或查询之前,SPROC 会逐渐变慢。
如果在每次执行前后打开和关闭SqlConnection
,则
SPROC 会逐渐变慢,直到 至少 8 分钟过去。
这只发生在少数存储过程中。一个是带有 2 个 JOINs
的简单 SELECT
查询,(SPROC 1) 另一个是 1600+ 行的大型 SPROC (SPROC 2)。
SPROC 1 的执行时间似乎永远不会超过 60 秒,SPROC 2 的执行时间永远不会超过 67 秒。 SPROC 1 初始执行时间不到一秒,SPROC 2 初始执行时间为 7 秒。
仅当 SPROC 在应用程序中使用相同的 SqlConnection
运行时才会发生这种情况。一旦使用了 2 个单独的 SqlConnection
对象,它们的行为与上述相同,但它们是独立的。在 SqlConnection1
上多次运行 SPROC 会逐渐变慢,但第一次在 SqlConnection2
上运行相同的 SPROC 时,它“很快”。在SqlConnection2
上多次运行时,它也会变慢。
如果应用程序在安装了 SQL Server 2008 R2(运行 Windows Server 2008)的同一台计算机上运行,则不会发生这种情况。执行时间始终一致。
在 Management Studio 中运行存储过程也不会因每次执行而变慢;它总是一致的。
清除执行计划缓存(在 SQL Server 中)对观察到的行为没有影响。
我们花了好几天的时间来缩小最初在一个更大的应用程序中观察到的问题,以便创建一个测试应用程序来轻松测试/重现它。
从我读过的here 来看,有4 到8 分钟的超时(在代码中调用SqlConnection.Close()
之后),此时它会关闭与数据源的数据库连接。这似乎符合我上面提到的场景 2。
这让我相信它与使用的SqlConnection
(以及与数据源的底层数据库连接)有关,因为在我的情况下启用了连接池,但是为什么我会观察到这种行为,以及如何我要修复它吗?
我们正在使用 .Net 2.0 框架,如果这有什么不同的话。
上面列出了很多细节,所以如果我需要澄清任何事情,请告诉我。
唯一有任何相似之处的 Stack Overflow 问题是 this,但与我的问题无关。
编辑: 以下代码在我的 WinForms 测试应用启动时执行:
SqlConnectionStringBuilder connectionStringBuilder = new SqlConnectionStringBuilder();
connectionStringBuilder.DataSource = m_DataSource;
connectionStringBuilder.InitialCatalog = m_InitialCatalog;
connectionStringBuilder.UserID = m_UserID;
connectionStringBuilder.Password = m_Password;
connectionStringBuilder.IntegratedSecurity = false;
connectionString = connectionStringBuilder.ConnectionString;
m_DatabaseConnection = new SqlConnection(connectionString);
然后我有 2 个按钮;其中一个调用上面提到的 SPROC 1,另一个调用另一个没有相同减速问题的 SPROC。在任一按钮单击时都会执行以下代码(唯一的区别是 SPROC 名称):
m_DatabaseConnection.Open();
m_DatabaseCommand = new SqlCommand("GetCompanies", m_DatabaseConnection);
m_DatabaseCommand.Parameters.AddWithValue("@StatusID", StatusID);
m_DatabaseCommand.CommandType = CommandType.StoredProcedure;
m_DatabaseCommand.CommandTimeout = 0;
SqlDataAdapter databaseDataAdapter = new SqlDataAdapter(m_DatabaseCommand);
DataSet databaseDataSet = new DataSet();
databaseDataAdapter.Fill(databaseDataSet);
m_DatabaseConnection.Close();
【问题讨论】:
这可能是在dba 上发布的更好的内容。 这一定是因为你的代码。但是,由于您没有向我们展示您的代码,我们无法为您提供帮助。 @JohnSaunders - 如果需要,我可以发布对 SPROC 1 的查询,但我认为没有人会从 1600+ 行 SPROC 2 中受益。 你能在 pastebin 上上传 SPROC2 吗?这样我们至少可以滚动查看可疑模式。 @usr - SPROC 2 可以在 here 找到。 SPROC 1 可以找到here,它使用一个视图。查询视图找到here。 【参考方案1】:这是我调试此问题的想法:
在 Dispose 连接后尝试调用 SqlConnection.ClearAllPools()。如果这样可以解决问题,那么问题肯定与特定连接有关。 接下来,将 SPROC 包含在显式事务中。 接下来,在调用 SPROC 之前调用 SqlConnection.ClearAllPools()。 SPROC 返回多少数据? 请发布您用于打开连接并执行 SPROC 的 C# 代码。 创建一个独立的控制台应用程序来重现您所看到的行为。这将(很可能)证明您的应用中存在问题,因为控制台应用可以正常运行。【讨论】:
我现在开始尝试您的建议,谢谢。 SPROC 1 最多返回 10 条记录,11 列。 SPROC 2 返回 2 条记录,共 15 列。 在 Disposing 之后调用 ClearAllPools() 确实可以解决问题,就像在调用 SPROC 之前调用它一样。将 SPROC 包含在显式事务中(在 .Net - TransactionScope 对象中)也可以解决问题。它“有效”,但真正导致问题的原因是什么?这一切都在我的独立 WinForms 测试应用程序中进行了测试。 当连接返回到池时,隔离级别不会重置。这可能是问题所在。尝试删除 tran,但在执行 proc 之前执行“设置事务隔离级别读取提交”。这能解决问题吗? 看到长存储过程后,我唯一能发现的是您正在使用表变量。这些没有导致计划极差的统计数据。我建议使用临时表。也许这样性能会变得更加可预测。但我没有发现任何可能导致“垃圾”随着时间的推移而积累的问题。您确定问题发生在 GetCompanies 上吗?它仅包含一个选择。试试这个:从这个过程中删除东西,直到它停止发生。 我之前确实有transactionOptions.IsolationLevel = System.Transactions.IsolationLevel.ReadCommitted
使用过TransactionScope
。我在执行 SPROC 之前删除了 tran 并执行了该命令,它也解决了问题。以上是关于同一 SPROC 的每次迭代执行时间变慢的主要内容,如果未能解决你的问题,请参考以下文章
Entity Framework EF 代码首先使用 ExecuteStoreCommand 执行 sproc 非常慢